From 191395f11762d4a85b925c26f727e0a777de4c4c Mon Sep 17 00:00:00 2001 From: akokoshn Date: Fri, 31 May 2024 18:20:22 +0300 Subject: [PATCH 1/7] Introduce zkevm_word Merger evmone into assigner library Use zkewvm_word instead of intx::uint256 --- CMakeLists.txt | 24 - lib/CMakeLists.txt | 1 - lib/assigner/CMakeLists.txt | 26 +- .../include/nil/blueprint/assigner.hpp | 28 +- .../nil/blueprint/assigner_interface.hpp | 35 + .../include/nil/blueprint/handler.hpp | 109 - .../include/nil/blueprint/handler_base.hpp | 41 - .../include/nil/blueprint/zkevm_word.hpp | 254 ++ lib/assigner/include/vm_host.h | 109 +- lib/assigner/src/handler_base.cpp | 22 - lib/assigner/src/vm_host.cpp | 100 - lib/assigner/test/assigner_test.cpp | 94 +- lib/evmone/CMakeLists.txt | 69 - lib/evmone/baseline.cpp | 269 +-- lib/evmone/baseline.hpp | 227 +- lib/evmone/eof.cpp | 3 +- lib/evmone/execution_state.hpp | 31 +- lib/evmone/instructions.hpp | 2039 +++++++++-------- lib/evmone/instructions_calls.cpp | 206 -- lib/evmone/instructions_storage.cpp | 144 -- lib/evmone/instructions_xmacro.hpp | 336 ++- lib/evmone/tracing.cpp | 239 -- lib/evmone/tracing.hpp | 70 - lib/evmone/vm.cpp | 15 - lib/evmone/vm.hpp | 15 - 25 files changed, 1997 insertions(+), 2509 deletions(-) create mode 100644 lib/assigner/include/nil/blueprint/assigner_interface.hpp delete mode 100644 lib/assigner/include/nil/blueprint/handler.hpp delete mode 100644 lib/assigner/include/nil/blueprint/handler_base.hpp create mode 100644 lib/assigner/include/nil/blueprint/zkevm_word.hpp delete mode 100644 lib/assigner/src/handler_base.cpp delete mode 100644 lib/assigner/src/vm_host.cpp delete mode 100644 lib/evmone/CMakeLists.txt delete mode 100644 lib/evmone/instructions_calls.cpp delete mode 100644 lib/evmone/instructions_storage.cpp delete mode 100644 lib/evmone/tracing.cpp delete mode 100644 lib/evmone/tracing.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fbe9d816..64f396a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,35 +116,11 @@ add_subdirectory(lib) # INSTALL -set(install_targets evmone) -if(TARGET evmone-standalone) - list(APPEND install_targets evmone-standalone) -endif() -if(TARGET evm-test) - list(APPEND install_targets evm-test) -endif() -if(TARGET evmone-bench) - list(APPEND install_targets evmone-bench) -endif() - -set_target_properties( - ${install_targets} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} -) - -install(TARGETS ${install_targets} EXPORT evmoneTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - install(DIRECTORY ${include_dir}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) set(CONFIG_PATH ${CMAKE_INSTALL_LIBDIR}/cmake/evm-assigner) set(TARGET_NAMESPACE evm-assigner::) -install(EXPORT evmoneTargets NAMESPACE ${TARGET_NAMESPACE} DESTINATION ${CONFIG_PATH}) install(EXPORT assignerTargets NAMESPACE ${TARGET_NAMESPACE} DESTINATION ${CONFIG_PATH}) include(CMakePackageConfigHelpers) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e114e685..0adfd026 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -4,5 +4,4 @@ find_package(intx CONFIG REQUIRED) -add_subdirectory(evmone) add_subdirectory(assigner) diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index 8f8d9fee..8ba9939d 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -5,19 +5,37 @@ cmake_policy(SET CMP0063 NEW) option(BUILD_ASSIGNER_TESTS "Build unit tests" FALSE) -add_library(${PROJECT_NAME} STATIC src/vm_host.cpp src/handler_base.cpp) +set(evmone_sources + ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline_instruction_table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.cpp +) + +add_library(${PROJECT_NAME} STATIC ${evmone_sources}) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) find_package(crypto3 REQUIRED) find_package(blueprint_crypto3 REQUIRED) target_include_directories(${PROJECT_NAME} PUBLIC $ + $ $ $) target_link_libraries(${PROJECT_NAME} - PUBLIC evmone crypto3::all blueprint - PRIVATE ethash::keccak) + PUBLIC evmc::evmc intx::intx crypto3::all blueprint ethash::keccak) + +set_target_properties( + ${PROJECT_NAME} + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_SOVERSION} +) + +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.cpp PROPERTIES COMPILE_DEFINITIONS PROJECT_VERSION="${PROJECT_VERSION}") # Install assigner headers install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ @@ -28,12 +46,10 @@ install(TARGETS ${PROJECT_NAME} EXPORT assignerTargets DESTINATION ${CMAKE_INSTA # Install evmone headers install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/execution_state.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/tracing.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/instructions_opcodes.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - if(BUILD_ASSIGNER_TESTS) add_subdirectory(test) endif() diff --git a/lib/assigner/include/nil/blueprint/assigner.hpp b/lib/assigner/include/nil/blueprint/assigner.hpp index 00ac3efc..c94e1f97 100644 --- a/lib/assigner/include/nil/blueprint/assigner.hpp +++ b/lib/assigner/include/nil/blueprint/assigner.hpp @@ -5,11 +5,13 @@ // LICENSE file in the root directory of this source tree. //---------------------------------------------------------------------------// -#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSERTS_HPP_ -#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSERTS_HPP_ +#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_HPP_ +#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_HPP_ + +#include -#include #include +#include namespace nil { namespace blueprint { @@ -19,25 +21,11 @@ namespace nil { using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; - assigner(std::vector> &assignments) { - handler_ptr = std::make_shared>(assignments); - } - - evmc::Result evaluate(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx, - evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) { - return nil::blueprint::evaluate(handler_ptr, c_vm, host, ctx, rev, msg, code, code_size); - } - - std::shared_ptr get_handler() { - return handler_ptr; - } + assigner(std::vector> &assignments): m_assignments(assignments) {} - private: - - std::shared_ptr handler_ptr; + std::vector> &m_assignments; }; - } // namespace blueprint } // namespace nil -#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSERTS_HPP_ +#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_HPP_ diff --git a/lib/assigner/include/nil/blueprint/assigner_interface.hpp b/lib/assigner/include/nil/blueprint/assigner_interface.hpp new file mode 100644 index 00000000..8878ac4b --- /dev/null +++ b/lib/assigner/include/nil/blueprint/assigner_interface.hpp @@ -0,0 +1,35 @@ +//---------------------------------------------------------------------------// +// Copyright (c) Nil Foundation and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. +//---------------------------------------------------------------------------// + +#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ +#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + static evmc::Result evaluate(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx, + evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size, + std::shared_ptr> assigner) { + auto vm = static_cast(c_vm); + const evmone::bytes_view container{code, code_size}; + const auto code_analysis = evmone::baseline::analyze(rev, container); + const auto data = code_analysis.eof_header.get_data(container); + auto state = std::make_unique>(*msg, rev, *host, ctx, container, data, assigner); + + auto res = execute(*vm, msg->gas, *state, code_analysis); + return evmc::Result{res}; + } + } // namespace blueprint +} // namespace nil + +#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ diff --git a/lib/assigner/include/nil/blueprint/handler.hpp b/lib/assigner/include/nil/blueprint/handler.hpp deleted file mode 100644 index 074a6d3e..00000000 --- a/lib/assigner/include/nil/blueprint/handler.hpp +++ /dev/null @@ -1,109 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) Nil Foundation and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. -//---------------------------------------------------------------------------// - -#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_HPP_ -#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_HPP_ - -#include -#include - -namespace nil { - namespace blueprint { - - template - class handler : public handler_base { - - public: - - using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; - using value_type = typename BlueprintFieldType::value_type; - - handler(std::vector> &assignments): - m_assignments(assignments) { - - } - - void set_witness(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) override { - assert(table_idx < m_assignments.size()); - m_assignments[table_idx].witness(column_idx, row_idx) = to_field(v); - } - - void set_constant(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) override { - assert(table_idx < m_assignments.size()); - m_assignments[table_idx].constant(column_idx, row_idx) = to_field(v); - } - - void set_public_input(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) override { - assert(table_idx < m_assignments.size()); - m_assignments[table_idx].public_input(column_idx, row_idx) = to_field(v); - } - - void set_selector(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) override { - assert(table_idx < m_assignments.size()); - m_assignments[table_idx].selector(column_idx, row_idx) = to_field(v); - } - - uint256 witness(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) override { - return to_uint256(m_assignments[table_idx].witness(column_idx, row_idx)); - } - - uint256 constant(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) override { - return to_uint256(m_assignments[table_idx].constant(column_idx, row_idx)); - } - - uint256 public_input(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) override { - return to_uint256(m_assignments[table_idx].public_input(column_idx, row_idx)); - } - - uint256 selector(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) override { - return to_uint256(m_assignments[table_idx].selector(column_idx, row_idx)); - } - - - public: - static value_type to_field(const uint256 intx_number) { - assert(intx_number < modulus); // TODO: replace with crypto3 assert - typename BlueprintFieldType::value_type field_value; - for (unsigned i = 0; i < 4; ++i) - { - typename BlueprintFieldType::integral_type word = intx_number[i]; - field_value += word << (64 * i); - } - return field_value; - } - - static uint256 to_uint256(const value_type field_value) { - return integral_to_uint256( - typename BlueprintFieldType::integral_type(field_value.data)); - } - - private: - static constexpr uint256 integral_to_uint256( - typename BlueprintFieldType::integral_type integral_value) - { - intx::uint256 res; - typename BlueprintFieldType::integral_type word_mask = 1; - word_mask = word_mask << 64U; - word_mask -= 1; // Got mask for lowest 64 bits - auto num = integral_value; - for (unsigned i = 0; i < 4; ++i) - { - res[i] = static_cast(num & word_mask); - num = num >> 64; - } - return res; - } - - static constexpr uint256 modulus = integral_to_uint256(BlueprintFieldType::modulus); - - std::vector> &m_assignments; - }; - - } // namespace blueprint -} // namespace nil - -#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_BASE_HPP_ diff --git a/lib/assigner/include/nil/blueprint/handler_base.hpp b/lib/assigner/include/nil/blueprint/handler_base.hpp deleted file mode 100644 index 6ff8f701..00000000 --- a/lib/assigner/include/nil/blueprint/handler_base.hpp +++ /dev/null @@ -1,41 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) Nil Foundation and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. -//---------------------------------------------------------------------------// - -#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_BASE_HPP_ -#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_BASE_HPP_ - -#include -#include - -#include - -namespace nil { - namespace blueprint { - using uint256 = intx::uint256; - - class handler_base { - - public: - - virtual void set_witness(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) = 0; - virtual void set_constant(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) = 0; - virtual void set_public_input(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) = 0; - virtual void set_selector(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx, const uint256& v) = 0; - - virtual uint256 witness(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) = 0; - virtual uint256 constant(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) = 0; - virtual uint256 public_input(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) = 0; - virtual uint256 selector(std::uint32_t table_idx, std::uint32_t column_idx, std::uint32_t row_idx) = 0; - }; - - evmc::Result evaluate(std::shared_ptr handler, evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx, - evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size); - - } // namespace blueprint -} // namespace nil - -#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_HANDLER_BASE_HPP_ diff --git a/lib/assigner/include/nil/blueprint/zkevm_word.hpp b/lib/assigner/include/nil/blueprint/zkevm_word.hpp new file mode 100644 index 00000000..9b614636 --- /dev/null +++ b/lib/assigner/include/nil/blueprint/zkevm_word.hpp @@ -0,0 +1,254 @@ +//---------------------------------------------------------------------------// +// Copyright (c) Nil Foundation and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. +//---------------------------------------------------------------------------// + +#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ZKEVM_WORD_HPP_ +#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ZKEVM_WORD_HPP_ + +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + + using Endianness = nil::marshalling::option::big_endian; + using TTypeBase = nil::marshalling::field_type; + + template + struct zkevm_word { + using column_type_t = typename crypto3::zk::snark::plonk_column; + using value_type = intx::uint256; + // constructors + zkevm_word() { + value = 0; + } + + zkevm_word(int v) { + value = v; + } + + zkevm_word(int64_t v) { + value = v; + } + + zkevm_word(size_t v) { + value = v; + } + + zkevm_word(const intx::uint256& v) { + value = v; + } + + zkevm_word(const evmc::uint256be& v) { + value = intx::be::load(v); + } + + zkevm_word(const evmc::address& addr) { + value = intx::be::load(addr); + } + + zkevm_word(const ethash::hash256& hash) { + value = intx::be::load(hash); + } + + zkevm_word(const uint8_t* data, size_t len) { + value = intx::be::unsafe::load(data); + } + + zkevm_word(column_type_t column_type, size_t column_idx, size_t row_idx, const assignment& tbl) { + typename BlueprintFieldType::value_type field_val; + switch (column_type) { + case column_type_t::witness: + field_val = tbl.witness(column_idx, row_idx); + break; + case column_type_t::constant: + field_val = tbl.constant(column_idx, row_idx); + break; + case column_type_t::public_input: + field_val = tbl.public_input(column_idx, row_idx); + break; + }; + auto field_container = nil::crypto3::marshalling::types::field_element(field_val); + evmc::uint256be data; + auto write_iter = data.bytes; + field_container.write(write_iter, field_container.length()); + value = value = intx::be::load(data); + } + + void put_into_assignment_table(column_type_t column_type, size_t column_idx, size_t row_idx, assignment& tbl) const { + auto field_container = nil::crypto3::marshalling::types::field_element(); + auto data = intx::be::store(value); + auto read_iter = data.bytes; + field_container.read(read_iter, field_container.length()); + } + + // operators + zkevm_word operator+(const zkevm_word& other) const { + return zkevm_word(value + other.value); + } + + zkevm_word operator-(const zkevm_word& other) const { + return zkevm_word(value - other.value); + } + + zkevm_word operator*(const zkevm_word& other) const { + return zkevm_word(value * other.value); + } + + zkevm_word operator/(const zkevm_word& other) const { + return zkevm_word(value / other.value); + } + + zkevm_word operator%(const zkevm_word& other) const { + return zkevm_word(value % other.value); + } + + zkevm_word sdiv(const zkevm_word& other) const { + return other.value != 0 ? intx::sdivrem(value, other.value).quot : 0; + } + + zkevm_word smod(const zkevm_word& other) const { + return other.value != 0 ? intx::sdivrem(value, other.value).rem : 0; + } + + zkevm_word addmod(const zkevm_word& other, const zkevm_word& m) const { + return m != 0 ? intx::addmod(value, other.value, m.value) : 0; + } + + zkevm_word mulmod(const zkevm_word& other, const zkevm_word& m) const { + return m != 0 ? intx::mulmod(value, other.value, m.value) : 0; + } + + zkevm_word exp(const zkevm_word& e) const { + return intx::exp(value, e.value); + } + + unsigned count_significant_bytes() const { + return intx::count_significant_bytes(value); + } + + bool slt(const zkevm_word& other) const { + return intx::slt(value, other.value); + } + + bool operator==(const zkevm_word& other) const { + return value == other.value; + } + + bool operator!=(const zkevm_word& other) const { + return value != other.value; + } + + bool operator!=(int v) const { + return value != v; + } + + bool operator<(const zkevm_word& other) const { + return value < other.value; + } + + bool operator<(int v) const { + return value < v; + } + + zkevm_word operator&(const zkevm_word& other) const { + return value & other.value; + } + + zkevm_word operator|(const zkevm_word& other) const { + return value | other.value; + } + + zkevm_word operator^(const zkevm_word& other) const { + return value ^ other.value; + } + + zkevm_word operator~() const { + return ~value; + } + + zkevm_word operator<<(const zkevm_word& other) const { + return value << other.value; + } + + zkevm_word operator>>(const zkevm_word& other) const { + return value >> other.value; + } + + zkevm_word operator<<(uint64_t v) const { + return value << v; + } + + zkevm_word operator>>(uint64_t v) const { + return value >> v; + } + + // convertions + uint64_t to_uint64(size_t i = 0) const { + return static_cast(value[i]); + } + + evmc::address to_address() const { + return intx::be::trunc(value); + } + + evmc::uint256be to_uint256be() const { + return intx::be::store(value); + } + + //partial size_t + void set_val(uint64_t v, size_t i) { + value[i] = v; + } + + // memory + template // uint8, uint16, uint32, uint64, intx::uint256 + void store(uint8_t* data) const { + intx::be::unsafe::store(data, static_cast(value)); + } + + void load_partial_data(const uint8_t* data, size_t len, size_t i) { + switch (len) { + case 1: + value[i] = intx::be::unsafe::load(data); + break; + case 2: + value[i] = intx::be::unsafe::load(data); + break; + case 3: + value[i] = intx::be::unsafe::load(data) >> 8; + break; + case 4: + value[i] = intx::be::unsafe::load(data); + break; + case 5: + case 6: + case 7: + case 8: + value[i] = intx::be::unsafe::load(data) >> (8 * (sizeof(uint64_t) - len)); + break; + }; + } + + const uint8_t* raw_data() const { + return reinterpret_cast(&value); + } + + uint64_t size() const { + return sizeof(value); + } + private: + intx::uint256 value; + }; + } // namespace blueprint +} // namespace nil +#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ZKEVM_WORD_HPP_ diff --git a/lib/assigner/include/vm_host.h b/lib/assigner/include/vm_host.h index 28fe7d56..6db75472 100644 --- a/lib/assigner/include/vm_host.h +++ b/lib/assigner/include/vm_host.h @@ -4,13 +4,17 @@ // Based on example host #include -#include +#include +#include #include #include #include #include +#include +#include + using namespace evmc::literals; namespace evmc @@ -38,20 +42,20 @@ using accounts = std::map; } // namespace evmc +template class VMHost : public evmc::Host { evmc::accounts accounts; evmc_tx_context tx_context{}; - std::shared_ptr handler; public: VMHost() = default; - explicit VMHost(evmc_tx_context& _tx_context, std::shared_ptr _handler) noexcept - : tx_context{_tx_context}, handler{_handler} + explicit VMHost(evmc_tx_context& _tx_context, std::shared_ptr> _assigner) noexcept + : tx_context{_tx_context}, assigner{_assigner} {} - VMHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts, std::shared_ptr _handler) noexcept - : accounts{_accounts}, tx_context{_tx_context}, handler{_handler} + VMHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts, std::shared_ptr> _assigner) noexcept + : accounts{_accounts}, tx_context{_tx_context}, assigner{_assigner} {} bool account_exists(const evmc::address& addr) const noexcept final @@ -212,16 +216,95 @@ class VMHost : public evmc::Host } private: - evmc::Result handle_call(const evmc_message& msg); - evmc::Result handle_create(const evmc_message& msg); - evmc::address calculate_address(const evmc_message& msg); -}; + std::shared_ptr> assigner; + evmc::Result handle_call(const evmc_message& msg) { + evmc_vm * vm = evmc_create_evmone(); + auto sender_iter = accounts.find(msg.sender); + if (sender_iter == accounts.end()) + { + // Sender account does not exist + return evmc::Result{EVMC_INTERNAL_ERROR}; + } + auto &sender_acc = sender_iter->second; + auto account_iter = accounts.find(msg.code_address); + if (account_iter == accounts.end()) + { + // Create account + accounts[msg.code_address] = {}; + } + auto& acc = accounts[msg.code_address]; + if (msg.kind == EVMC_CALL) { + auto value_to_transfer = nil::blueprint::zkevm_word(msg.value); + auto balance = nil::blueprint::zkevm_word(sender_acc.balance); + // Balance was already checked in evmone, so simply adjust it + sender_acc.balance = (balance - value_to_transfer).to_uint256be(); + acc.balance = (value_to_transfer + nil::blueprint::zkevm_word(acc.balance)).to_uint256be(); + } + if (acc.code.empty()) + { + return evmc::Result{EVMC_SUCCESS, msg.gas, 0, msg.input_data, msg.input_size}; + } + // TODO: handle precompiled contracts + evmc::Result res = nil::blueprint::evaluate(vm, &get_interface(), to_context(), + EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size(), assigner); + return res; + } -extern "C" { + evmc::Result handle_create(const evmc_message& msg) { + evmc::address new_contract_address = calculate_address(msg); + if (accounts.find(new_contract_address) != accounts.end()) + { + // Address collision + return evmc::Result{EVMC_FAILURE}; + } + accounts[new_contract_address] = {}; + if (msg.input_size == 0) + { + return evmc::Result{EVMC_SUCCESS, msg.gas, 0, new_contract_address}; + } + evmc::VM vm{evmc_create_evmone()}; + evmc_message init_msg(msg); + init_msg.kind = EVMC_CALL; + init_msg.recipient = new_contract_address; + init_msg.sender = msg.sender; + init_msg.input_size = 0; + evmc::Result res = nil::blueprint::evaluate(vm.get_raw_pointer(), &get_interface(), to_context(), + EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size, assigner); + if (res.status_code == EVMC_SUCCESS) + { + accounts[new_contract_address].code = + std::vector(res.output_data, res.output_data + res.output_size); + } + res.create_address = new_contract_address; + return res; + } -evmc_host_context* vm_host_create_context(evmc_tx_context tx_context, std::shared_ptr handler); -void vm_host_destroy_context(evmc_host_context* context); + evmc::address calculate_address(const evmc_message& msg) { + // TODO: Implement for CREATE opcode, for now the result is only correct for CREATE2 + // CREATE requires rlp encoding + auto seed = nil::blueprint::zkevm_word(msg.create2_salt); + auto hash = nil::blueprint::zkevm_word(ethash::keccak256(msg.input_data, msg.input_size)); + auto sender = nil::blueprint::zkevm_word(msg.sender); + auto sum = nil::blueprint::zkevm_word(0xff) + seed + hash + sender; + auto rehash = ethash::keccak256(sum.raw_data(), sum.size()); + // Result address is the last 20 bytes of the hash + evmc::address res; + std::memcpy(res.bytes, rehash.bytes + 12, 20); + return res; + } +}; + +template +evmc_host_context* vm_host_create_context(evmc_tx_context tx_context, std::shared_ptr> assigner) { + auto host = new VMHost{tx_context, assigner}; + return host->to_context(); } +template +void vm_host_destroy_context(evmc_host_context* context) { + delete evmc::Host::from_context>(context); +} + + #endif // EVM_ASSIGNER_LIB_ASSIGNER_INCLUDE_VM_HOST_H_ diff --git a/lib/assigner/src/handler_base.cpp b/lib/assigner/src/handler_base.cpp deleted file mode 100644 index 17b8a6fc..00000000 --- a/lib/assigner/src/handler_base.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include -#include - -namespace nil { - namespace blueprint { - evmc::Result evaluate(std::shared_ptr handler, evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx, - evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) { - - auto vm = static_cast(c_vm); - const evmone::bytes_view container{code, code_size}; - const auto code_analysis = evmone::baseline::analyze(rev, container); - const auto data = code_analysis.eof_header.get_data(container); - auto state = std::make_unique(*msg, rev, *host, ctx, container, data); - state->set_handler(handler); - - auto res = execute(*vm, msg->gas, *state, code_analysis); - return evmc::Result{res}; - } - } // namespace blueprint -} // namespace nil diff --git a/lib/assigner/src/vm_host.cpp b/lib/assigner/src/vm_host.cpp deleted file mode 100644 index c938cde1..00000000 --- a/lib/assigner/src/vm_host.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "vm_host.h" - -#include -#include -#include - -extern "C" { - -evmc_host_context* vm_host_create_context(evmc_tx_context tx_context, std::shared_ptr handler) -{ - auto host = new VMHost{tx_context, handler}; - return host->to_context(); -} - -void vm_host_destroy_context(evmc_host_context* context) -{ - delete evmc::Host::from_context(context); -} -} - -evmc::Result VMHost::handle_call(const evmc_message& msg) -{ - evmc_vm * vm = evmc_create_evmone(); - auto sender_iter = accounts.find(msg.sender); - if (sender_iter == accounts.end()) - { - // Sender account does not exist - return evmc::Result{EVMC_INTERNAL_ERROR}; - } - auto &sender_acc = sender_iter->second; - auto account_iter = accounts.find(msg.code_address); - if (account_iter == accounts.end()) - { - // Create account - accounts[msg.code_address] = {}; - } - auto& acc = accounts[msg.code_address]; - if (msg.kind == EVMC_CALL) { - auto value_to_transfer = intx::be::load(msg.value); - auto balance = intx::be::load(sender_acc.balance); - // Balance was already checked in evmone, so simply adjust it - sender_acc.balance = intx::be::store(balance - value_to_transfer); - acc.balance = intx::be::store( - value_to_transfer + intx::be::load(acc.balance)); - } - if (acc.code.empty()) - { - return evmc::Result{EVMC_SUCCESS, msg.gas, 0, msg.input_data, msg.input_size}; - } - // TODO: handle precompiled contracts - auto res = evaluate(handler, vm, &get_interface(), to_context(), - EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size()); - return res; -} - -evmc::Result VMHost::handle_create(const evmc_message& msg) -{ - evmc::address new_contract_address = calculate_address(msg); - if (accounts.find(new_contract_address) != accounts.end()) - { - // Address collision - return evmc::Result{EVMC_FAILURE}; - } - accounts[new_contract_address] = {}; - if (msg.input_size == 0) - { - return evmc::Result{EVMC_SUCCESS, msg.gas, 0, new_contract_address}; - } - evmc::VM vm{evmc_create_evmone()}; - evmc_message init_msg(msg); - init_msg.kind = EVMC_CALL; - init_msg.recipient = new_contract_address; - init_msg.sender = msg.sender; - init_msg.input_size = 0; - auto res = evaluate(handler, vm.get_raw_pointer(), &get_interface(), to_context(), - EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size); - - if (res.status_code == EVMC_SUCCESS) - { - accounts[new_contract_address].code = - std::vector(res.output_data, res.output_data + res.output_size); - } - res.create_address = new_contract_address; - return res; -} - -evmc::address VMHost::calculate_address(const evmc_message& msg) -{ - // TODO: Implement for CREATE opcode, for now the result is only correct for CREATE2 - // CREATE requires rlp encoding - auto seed = intx::be::load(msg.create2_salt); - auto hash = intx::be::load(ethash::keccak256(msg.input_data, msg.input_size)); - auto sender = intx::be::load(msg.sender); - auto sum = 0xff + seed + hash + sender; - auto rehash = ethash::keccak256(reinterpret_cast(&sum), sizeof(sum)); - // Result address is the last 20 bytes of the hash - evmc::address res; - std::memcpy(res.bytes, rehash.bytes + 12, 20); - return res; -} diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index ee5005f2..eb6da1b9 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -31,7 +31,7 @@ class AssignerTest : public testing::Test assignments.emplace_back(desc); // for check create, call op codes, where message depth = 1 assigner_ptr = - std::make_unique>(assignments); + std::make_shared>(assignments); vm = evmc_create_evmone(); @@ -64,16 +64,16 @@ class AssignerTest : public testing::Test host_interface = &evmc::Host::get_interface(); - ctx = vm_host_create_context(tx_context, assigner_ptr->get_handler()); + ctx = vm_host_create_context(tx_context, assigner_ptr); } static void TearDownTestSuite() { assigner_ptr.reset(); - vm_host_destroy_context(ctx); + vm_host_destroy_context(ctx); } - static std::unique_ptr> assigner_ptr; + static std::shared_ptr> assigner_ptr; static std::vector> assignments; static const struct evmc_host_interface* host_interface; static struct evmc_host_context* ctx; @@ -82,7 +82,7 @@ class AssignerTest : public testing::Test static struct evmc_message msg; }; -std::unique_ptr> +std::shared_ptr> AssignerTest::assigner_ptr; std::vector> AssignerTest::assignments; @@ -92,27 +92,46 @@ struct evmc_vm* AssignerTest::vm; evmc_revision AssignerTest::rev = {}; struct evmc_message AssignerTest::msg; -using intx::operator""_u256; +/*std::string to_string(const uint8_t* bytes, size_t len) { + std::ostringstream oss; + for (int i = 0; i < len; i++) { + oss << bytes[i]; + } + return oss.str(); +} -TEST_F(AssignerTest, conversions) +TEST_F(AssignerTest, conversions_uint256be_to_field) { - intx::uint256 intx_number; - intx_number[2] = 10; // Some big number, 10 << 128 - auto field = nil::blueprint::handler::to_field(intx_number); - // Compare string representations - std::ostringstream oss; - oss << field.data; - EXPECT_EQ(intx::to_string(intx_number), oss.str()); - oss.str(""); - - // Modify field and test conversion to uint256 - field *= 3; - intx_number = nil::blueprint::handler::to_uint256(field); - oss << field.data; - EXPECT_EQ(intx::to_string(intx_number), oss.str()); - oss.str(""); + evmc::uint256be uint256be_number; + uint256be_number.bytes[2] = 10; // Some big number, 10 << 128 + // conversion to field + auto field = nil::blueprint::to_field(uint256be_number); + // conversion back to uint256be + evmc::uint256be uint256be_result = nil::blueprint::to_uint256be(field); + // check if same + EXPECT_EQ(to_string(uint256be_number.bytes, 32), to_string(uint256be_result.bytes, 32)); } +TEST_F(AssignerTest, conversions_address_to_field) +{ + evmc::address address; + address.bytes[19] = 10; + // conversion to field + auto field = nil::blueprint::to_field(address); + // conversion back to uint256be + evmc::address address_result = nil::blueprint::to_address(field); + // check if same + EXPECT_EQ(to_string(address.bytes, 20), to_string(address_result.bytes, 20)); +} + +TEST_F(AssignerTest, conversions_field_to_uint64) +{ + BlueprintFieldType::value_type field = 10; + // conversion to uint64_t + auto uint64_number = nil::blueprint::to_uint64(field); + EXPECT_EQ(uint64_number, 10); +}*/ + TEST_F(AssignerTest, mul) { std::vector code = { @@ -123,7 +142,7 @@ TEST_F(AssignerTest, mul) { evmone::OP_MUL, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(0, 0), 8); EXPECT_EQ(assignments[0].witness(0, 1), 4); } @@ -137,9 +156,8 @@ TEST_F(AssignerTest, callvalue_calldataload) index, evmone::OP_CALLDATALOAD, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); - EXPECT_EQ(nil::blueprint::handler::to_uint256(assignments[0].witness(1, 0)), - intx::be::load(msg.value)); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + //EXPECT_EQ(assignments[0].witness(1, 0), nil::blueprint::to_field(msg.value)); EXPECT_EQ(assignments[0].witness(1, 1), index); } @@ -151,7 +169,7 @@ TEST_F(AssignerTest, DISABLED_dataload) { index, evmone::OP_DATALOAD, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(1, 2), index); } @@ -169,7 +187,7 @@ TEST_F(AssignerTest, mstore_load) index, evmone::OP_MLOAD, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(2, 0), value); EXPECT_EQ(assignments[0].witness(2, 1), index); EXPECT_EQ(assignments[0].witness(2, 2), value); @@ -189,7 +207,7 @@ TEST_F(AssignerTest, sstore_load) key, evmone::OP_SLOAD, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(3, 0), value); EXPECT_EQ(assignments[0].witness(3, 1), key); EXPECT_EQ(assignments[0].witness(3, 2), value); @@ -209,7 +227,7 @@ TEST_F(AssignerTest, DISABLED_tstore_load) { key, evmone::OP_TLOAD, }; - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(4, 0), value); EXPECT_EQ(assignments[0].witness(4, 1), key); EXPECT_EQ(assignments[0].witness(4, 2), value); @@ -262,12 +280,11 @@ TEST_F(AssignerTest, create) { ASSERT_NE(push13_it, code.end()); size_t push13_idx = static_cast(push13_it - code.begin()); - auto contract_code = 0x63FFFFFFFF60005260046000F3_u256; - auto byte_container = intx::be::store(contract_code); + std::vector contract_code = {0x63, 0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x00, 0xf3}; // Code is in the last 13 bytes of the container - code.insert(code.begin() + static_cast(push13_idx) + 1, &byte_container.bytes[32-13], &byte_container.bytes[32]); + code.insert(code.begin() + static_cast(push13_idx) + 1, contract_code.begin(), contract_code.end()); - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); // Check stored witnesses of MSTORE instruction at depth 1 EXPECT_EQ(assignments[1].witness(2, 1), 0); EXPECT_EQ(assignments[1].witness(2, 0), 0xFFFFFFFF); @@ -330,12 +347,11 @@ TEST_F(AssignerTest, call) { ASSERT_NE(push17_it, code.end()); size_t push17_idx = static_cast(push17_it - code.begin()); - auto contract_code = 0x67600035600757FE5B60005260086018F3_u256; - auto bytes = intx::be::store(contract_code); + std::vector contract_code = {0x67, 0x60, 0x00, 0x35, 0x60, 0x07, 0x57, 0xfe, 0x5b, 0x60, 0x00, 0x52, 0x60, 0x08, 0x60, 0x18, 0xf3}; // Code is in the last 13 bytes of the container - code.insert(code.begin() + static_cast(push17_idx) + 1, &bytes.bytes[32-17], &bytes.bytes[32]); + code.insert(code.begin() + static_cast(push17_idx) + 1, contract_code.begin(), contract_code.end()); - assigner_ptr->evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size()); + nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); // Check stored witness of CALLDATALOAD instruction at depth 1 EXPECT_EQ(assignments[1].witness(1, 1), 0); } diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt deleted file mode 100644 index a47c258c..00000000 --- a/lib/evmone/CMakeLists.txt +++ /dev/null @@ -1,69 +0,0 @@ -# evmone: Fast Ethereum Virtual Machine implementation -# Copyright 2019 The evmone Authors. -# SPDX-License-Identifier: Apache-2.0 - -include(LibraryTools) - -set(evmone_sources - ${include_dir}/evmone/evmone.h - baseline.cpp - baseline.hpp - baseline_instruction_table.cpp - baseline_instruction_table.hpp - eof.cpp - eof.hpp - instructions.hpp - instructions_calls.cpp - instructions_opcodes.hpp - instructions_storage.cpp - instructions_traits.hpp - instructions_xmacro.hpp - opcodes_helpers.h - tracing.cpp - tracing.hpp - vm.cpp - vm.hpp -) - -add_library(evmone ${evmone_sources}) - -target_compile_features(evmone PUBLIC cxx_std_20) -target_link_libraries(evmone PUBLIC evmc::evmc intx::intx PRIVATE ethash::keccak) -target_include_directories(evmone PUBLIC - $$ - $ - PRIVATE ${FPRINTER_DIR} -) - -if(EVMONE_X86_64_ARCH_LEVEL GREATER_EQUAL 2) - # Add CPU architecture runtime check. The EVMONE_X86_64_ARCH_LEVEL has a valid value. - target_sources(evmone PRIVATE cpu_check.cpp) - set_source_files_properties(cpu_check.cpp PROPERTIES COMPILE_DEFINITIONS EVMONE_X86_64_ARCH_LEVEL=${EVMONE_X86_64_ARCH_LEVEL}) -endif() - -if(CABLE_COMPILER_GNULIKE AND DISABLE_EXCEPTIONS) - target_compile_options( - evmone PRIVATE - -fno-exceptions - $<$:-Wstack-usage=3000> - ) - if(NOT SANITIZE MATCHES undefined) - # RTTI can be disabled except for UBSan which checks vptr integrity. - target_compile_options(evmone PRIVATE -fno-rtti) - endif() -endif() -set_target_properties( - evmone - PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_SOVERSION} -) - -if(NOT SANITIZE) - # On Linux, check if all symbols in evmone are resolved during linking. - target_link_options(evmone PRIVATE $<$:LINKER:--no-undefined>) -endif() - -set_source_files_properties(vm.cpp PROPERTIES COMPILE_DEFINITIONS PROJECT_VERSION="${PROJECT_VERSION}") - -add_standalone_library(evmone) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index 85e72ecb..aec90118 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -3,12 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "baseline.hpp" -#include "baseline_instruction_table.hpp" -#include "eof.hpp" -#include "execution_state.hpp" -#include "instructions.hpp" -#include "vm.hpp" -#include #ifdef NDEBUG #define release_inline gnu::always_inline, msvc::forceinline @@ -39,7 +33,7 @@ CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code) const auto op = code[i]; if (static_cast(op) >= OP_PUSH1) // If any PUSH opcode (see explanation above). i += op - size_t{OP_PUSH1 - 1}; // Skip PUSH data. - else if (INTX_UNLIKELY(op == OP_JUMPDEST)) + else if (op == OP_JUMPDEST) map[i] = true; } @@ -89,265 +83,4 @@ CodeAnalysis analyze(evmc_revision rev, bytes_view code) return analyze_legacy(code); return analyze_eof1(code); } - -namespace -{ -/// Checks instruction requirements before execution. -/// -/// This checks: -/// - if the instruction is defined -/// - if stack height requirements are fulfilled (stack overflow, stack underflow) -/// - charges the instruction base gas cost and checks is there is any gas left. -/// -/// @tparam Op Instruction opcode. -/// @param cost_table Table of base gas costs. -/// @param [in,out] gas_left Gas left. -/// @param stack_top Pointer to the stack top item. -/// @param stack_bottom Pointer to the stack bottom. -/// The stack height is stack_top - stack_bottom. -/// @return Status code with information which check has failed -/// or EVMC_SUCCESS if everything is fine. -template -inline evmc_status_code check_requirements(const CostTable& cost_table, int64_t& gas_left, - const uint256* stack_top, const uint256* stack_bottom) noexcept -{ - static_assert( - !instr::has_const_gas_cost(Op) || instr::gas_costs[EVMC_FRONTIER][Op] != instr::undefined, - "undefined instructions must not be handled by check_requirements()"); - - auto gas_cost = instr::gas_costs[EVMC_FRONTIER][Op]; // Init assuming const cost. - if constexpr (!instr::has_const_gas_cost(Op)) - { - gas_cost = cost_table[Op]; // If not, load the cost from the table. - - // Negative cost marks an undefined instruction. - // This check must be first to produce correct error code. - if (INTX_UNLIKELY(gas_cost < 0)) - return EVMC_UNDEFINED_INSTRUCTION; - } - - // Check stack requirements first. This is order is not required, - // but it is nicer because complete gas check may need to inspect operands. - if constexpr (instr::traits[Op].stack_height_change > 0) - { - static_assert(instr::traits[Op].stack_height_change == 1, - "unexpected instruction with multiple results"); - if (INTX_UNLIKELY(stack_top == stack_bottom + StackSpace::limit)) - return EVMC_STACK_OVERFLOW; - } - if constexpr (instr::traits[Op].stack_height_required > 0) - { - // Check stack underflow using pointer comparison <= (better optimization). - static constexpr auto min_offset = instr::traits[Op].stack_height_required - 1; - if (INTX_UNLIKELY(stack_top <= stack_bottom + min_offset)) - return EVMC_STACK_UNDERFLOW; - } - - if (INTX_UNLIKELY((gas_left -= gas_cost) < 0)) - return EVMC_OUT_OF_GAS; - - return EVMC_SUCCESS; -} - - -/// The execution position. -struct Position -{ - code_iterator code_it; ///< The position in the code. - uint256* stack_top; ///< The pointer to the stack top. -}; - -/// Helpers for invoking instruction implementations of different signatures. -/// @{ -[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop) noexcept, Position pos, - int64_t& /*gas*/, ExecutionState& /*state*/) noexcept -{ - instr_fn(pos.stack_top); - return pos.code_it + 1; -} - -[[release_inline]] inline code_iterator invoke( - Result (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, - ExecutionState& state) noexcept -{ - const auto o = instr_fn(pos.stack_top, gas, state); - gas = o.gas_left; - if (o.status != EVMC_SUCCESS) - { - state.status = o.status; - return nullptr; - } - return pos.code_it + 1; -} - -[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop, ExecutionState&) noexcept, - Position pos, int64_t& /*gas*/, ExecutionState& state) noexcept -{ - instr_fn(pos.stack_top, state); - return pos.code_it + 1; -} - -[[release_inline]] inline code_iterator invoke( - code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos, - int64_t& /*gas*/, ExecutionState& state) noexcept -{ - return instr_fn(pos.stack_top, state, pos.code_it); -} - -[[release_inline]] inline code_iterator invoke( - TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, - ExecutionState& state) noexcept -{ - const auto result = instr_fn(pos.stack_top, gas, state); - gas = result.gas_left; - state.status = result.status; - return nullptr; -} -/// @} - -/// A helper to invoke the instruction implementation of the given opcode Op. -template -[[release_inline]] inline Position invoke(const CostTable& cost_table, const uint256* stack_bottom, - Position pos, int64_t& gas, ExecutionState& state) noexcept -{ - if (const auto status = check_requirements(cost_table, gas, pos.stack_top, stack_bottom); - status != EVMC_SUCCESS) - { - state.status = status; - return {nullptr, pos.stack_top}; - } - const auto new_pos = invoke(instr::core::impl, pos, gas, state); - const auto new_stack_top = pos.stack_top + instr::traits[Op].stack_height_change; - return {new_pos, new_stack_top}; -} - -/// PRINTF opcode consumes variable number of operands, determined dynamically by the format string. -/// This `invoke` specialization determines the stack shift according to the result of the Opcode -/// handler. -template <> -[[release_inline]] inline Position invoke(const CostTable& cost_table, const uint256* stack_bottom, - Position pos, int64_t& gas, ExecutionState& state) noexcept -{ - static constexpr Opcode Op = Opcode::OP_PRINTF; - - if (const auto status = check_requirements(cost_table, gas, pos.stack_top, stack_bottom); - status != EVMC_SUCCESS) - { - state.status = status; - return {nullptr, pos.stack_top}; - } - auto res = instr::core::impl(pos.stack_top, gas, state); - gas = res.gas_left; - code_iterator code_it; - if (res.status != EVMC_SUCCESS) - { - state.status = res.status; - code_it = nullptr; - } else - { - code_it = pos.code_it + 1; - } - - const auto new_stack_top = pos.stack_top - res.stack_pop; - return {code_it, new_stack_top}; -} - - -template -int64_t dispatch(const CostTable& cost_table, ExecutionState& state, int64_t gas, - const uint8_t* code, Tracer* tracer = nullptr) noexcept -{ - const auto stack_bottom = state.stack_space.bottom(); - - // Code iterator and stack top pointer for interpreter loop. - Position position{code, stack_bottom}; - - while (true) // Guaranteed to terminate because padded code ends with STOP. - { - if constexpr (TracingEnabled) - { - const auto offset = static_cast(position.code_it - code); - const auto stack_height = static_cast(position.stack_top - stack_bottom); - if (offset < state.original_code.size()) // Skip STOP from code padding. - { - tracer->notify_instruction_start( - offset, position.stack_top, stack_height, gas, state); - } - } - - const auto op = *position.code_it; - switch (op) - { -#define ON_OPCODE(OPCODE) \ - case OPCODE: \ - ASM_COMMENT(OPCODE); \ - if (const auto next = invoke(cost_table, stack_bottom, position, gas, state); \ - next.code_it == nullptr) \ - { \ - return gas; \ - } \ - else \ - { \ - /* Update current position only when no error, \ - this improves compiler optimization. */ \ - position = next; \ - } \ - break; - - MAP_OPCODES -#undef ON_OPCODE - - default: - state.status = EVMC_UNDEFINED_INSTRUCTION; - return gas; - } - } - intx::unreachable(); -} - -} // namespace - -evmc_result execute( - const VM& vm, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept -{ - state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. - - const auto code = analysis.executable_code; - - const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version); - - auto* tracer = vm.get_tracer(); - if (INTX_UNLIKELY(tracer != nullptr)) - { - tracer->notify_execution_start(state.rev, *state.msg, analysis.executable_code); - gas = dispatch(cost_table, state, gas, code.data(), tracer); - } - else - { - gas = dispatch(cost_table, state, gas, code.data()); - } - - const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; - const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; - - assert(state.output_size != 0 || state.output_offset == 0); - const auto result = evmc::make_result(state.status, gas_left, gas_refund, - state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size); - - if (INTX_UNLIKELY(tracer != nullptr)) - tracer->notify_execution_end(result); - - return result; -} - -evmc_result execute(evmc_vm* c_vm, 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 -{ - auto vm = static_cast(c_vm); - const bytes_view container{code, code_size}; - const auto code_analysis = analyze(rev, container); - const auto data = code_analysis.eof_header.get_data(container); - auto state = std::make_unique(*msg, rev, *host, ctx, container, data); - return execute(*vm, msg->gas, *state, code_analysis); -} } // namespace evmone::baseline diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index 01e49737..8ac703aa 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -4,18 +4,34 @@ #pragma once #include "eof.hpp" +#include "baseline_instruction_table.hpp" +#include "execution_state.hpp" +#include "instructions.hpp" +#include "vm.hpp" + #include #include #include #include #include -#include +#ifdef NDEBUG +#define release_inline gnu::always_inline, msvc::forceinline +#else +#define release_inline +#endif + +#if defined(__GNUC__) +#define ASM_COMMENT(COMMENT) asm("# " #COMMENT) // NOLINT(hicpp-no-assembler) +#else +#define ASM_COMMENT(COMMENT) +#endif namespace evmone { using bytes_view = std::basic_string_view; +template class ExecutionState; class VM; @@ -56,13 +72,214 @@ CodeAnalysis analyze(evmc_revision rev, bytes_view code); /// Analyze the code to build the bitmap of valid JUMPDEST locations. EVMC_EXPORT CodeAnalysis analyze(evmc_revision rev, bytes_view code); +/// The execution position. +template +struct Position +{ + code_iterator code_it; ///< The position in the code. + nil::blueprint::zkevm_word* stack_top; ///< The pointer to the stack top. +}; + +/// Checks instruction requirements before execution. +/// +/// This checks: +/// - if the instruction is defined +/// - if stack height requirements are fulfilled (stack overflow, stack underflow) +/// - charges the instruction base gas cost and checks is there is any gas left. +/// +/// @tparam Op Instruction opcode. +/// @param cost_table Table of base gas costs. +/// @param [in,out] gas_left Gas left. +/// @param stack_top Pointer to the stack top item. +/// @param stack_bottom Pointer to the stack bottom. +/// The stack height is stack_top - stack_bottom. +/// @return Status code with information which check has failed +/// or EVMC_SUCCESS if everything is fine. +template +inline evmc_status_code check_requirements(const CostTable& cost_table, int64_t& gas_left, + const nil::blueprint::zkevm_word* stack_top, + const nil::blueprint::zkevm_word* stack_bottom, + Opcode op) noexcept +{ + assert( + !instr::has_const_gas_cost(op) || instr::gas_costs[EVMC_FRONTIER][op] != instr::undefined); + + auto gas_cost = instr::gas_costs[EVMC_FRONTIER][op]; // Init assuming const cost. + if (!instr::has_const_gas_cost(op)) + { + gas_cost = cost_table[op]; // If not, load the cost from the table. + + // Negative cost marks an undefined instruction. + // This check must be first to produce correct error code. + if (gas_cost < 0) + return EVMC_UNDEFINED_INSTRUCTION; + } + + // Check stack requirements first. This is order is not required, + // but it is nicer because complete gas check may need to inspect operands. + if (instr::traits[op].stack_height_change > 0) + { + assert(instr::traits[op].stack_height_change == 1); + if (stack_top == stack_bottom + StackSpace::limit) + return EVMC_STACK_OVERFLOW; + } + if (instr::traits[op].stack_height_required > 0) + { + // Check stack underflow using pointer comparison <= (better optimization). + const auto min_offset = instr::traits[op].stack_height_required - 1; + if (stack_top <= stack_bottom + min_offset) + return EVMC_STACK_UNDERFLOW; + } + + if ((gas_left -= gas_cost) < 0) + return EVMC_OUT_OF_GAS; + + return EVMC_SUCCESS; +} + +/// Helpers for invoking instruction implementations of different signatures. +/// @{ +template +[[release_inline]] inline code_iterator invoke(std::nullptr_t /*fn*/, Position pos, + int64_t& /*gas*/, ExecutionState& /*state*/) noexcept +{ + return nullptr; +} + +template +[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop) noexcept, Position pos, + int64_t& /*gas*/, ExecutionState& /*state*/) noexcept +{ + instr_fn(pos.stack_top); + return pos.code_it + 1; +} + +template +[[release_inline]] inline code_iterator invoke( + Result (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, + ExecutionState& state) noexcept +{ + const auto o = instr_fn(pos.stack_top, gas, state); + gas = o.gas_left; + if (o.status != EVMC_SUCCESS) + { + state.status = o.status; + return nullptr; + } + return pos.code_it + 1; +} + +template +[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop, ExecutionState&) noexcept, + Position pos, int64_t& /*gas*/, ExecutionState& state) noexcept +{ + instr_fn(pos.stack_top, state); + return pos.code_it + 1; +} + +template +[[release_inline]] inline code_iterator invoke( + code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos, + int64_t& /*gas*/, ExecutionState& state) noexcept +{ + return instr_fn(pos.stack_top, state, pos.code_it); +} + +template +[[release_inline]] inline code_iterator invoke( + TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, + ExecutionState& state) noexcept +{ + const auto result = instr_fn(pos.stack_top, gas, state); + gas = result.gas_left; + state.status = result.status; + return nullptr; +} +/// A helper to invoke the instruction implementation of the given opcode Op. +template +[[release_inline]] inline Position invoke(const CostTable& cost_table, const nil::blueprint::zkevm_word* stack_bottom, + Position pos, int64_t& gas, ExecutionState& state, const uint8_t& op) noexcept +{ + if (const auto status = check_requirements(cost_table, gas, pos.stack_top, stack_bottom, Opcode(op)); + status != EVMC_SUCCESS) + { + state.status = status; + return {nullptr, pos.stack_top}; + } + //const auto new_pos = invoke(instr::core::operation::template push<1>, pos, gas, state); + code_iterator new_pos = pos.code_it; + switch (op) + { +#undef ON_OPCODE_IDENTIFIER +#define ON_OPCODE_IDENTIFIER(OPCODE, IDENTIFIER) \ +case OPCODE: \ + ASM_COMMENT(OPCODE); \ + new_pos = invoke(instr::core::IDENTIFIER, pos, gas, state); \ + break; + MAP_OPCODES +#undef ON_OPCODE_IDENTIFIER + + default: + state.status = EVMC_UNDEFINED_INSTRUCTION; + return {nullptr, pos.stack_top};; + } + const auto new_stack_top = pos.stack_top + instr::traits[op].stack_height_change; + return Position(new_pos, new_stack_top); +} +/// @} + +template +int64_t dispatch(const CostTable& cost_table, ExecutionState& state, int64_t gas, + const uint8_t* code) noexcept +{ + const auto stack_bottom = state.stack_space.bottom(); + + // Code iterator and stack top pointer for interpreter loop. + Position position{code, stack_bottom}; + + while (true) // Guaranteed to terminate because padded code ends with STOP. + { + const auto op = *position.code_it; + const auto next = invoke(cost_table, stack_bottom, position, gas, state, op); + if (next.code_it == nullptr) + { + return gas; + } else { + /*Update current position only when no error, + this improves compiler optimization.*/ + position = next; + } + } + //intx::unreachable(); +} + /// Executes in Baseline interpreter using EVMC-compatible parameters. -evmc_result execute(evmc_vm* vm, 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; +static evmc_result execute(evmc_vm* c_vm, 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 { + const auto result = evmc::make_result(EVMC_SUCCESS, msg->gas, msg->gas, nullptr, 0); + return result; +} /// Executes in Baseline interpreter on the given external and initialized state. -EVMC_EXPORT evmc_result execute( - const VM&, int64_t gas_limit, ExecutionState& state, const CodeAnalysis& analysis) noexcept; +template +static evmc_result execute(const VM&, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept { + state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. + + const auto code = analysis.executable_code; + + const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version); + + gas = dispatch(cost_table, state, gas, code.data()); + + const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; + const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; + + assert(state.output_size != 0 || state.output_offset == 0); + const auto result = evmc::make_result(state.status, gas_left, gas_refund, + state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size); + + return result; +} } // namespace baseline } // namespace evmone diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index e4848586..6f41947c 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -6,7 +6,6 @@ #include "baseline_instruction_table.hpp" #include "instructions_traits.hpp" -#include #include #include #include @@ -63,7 +62,7 @@ EOFValidationError get_section_missing_error(uint8_t section_id) noexcept case DATA_SECTION: return EOFValidationError::data_section_missing; default: - intx::unreachable(); + return EOFValidationError::impossible; } } diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index dd96e9a6..6e774584 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -4,11 +4,11 @@ #pragma once #include -#include #include #include -#include +#include +#include namespace evmone { @@ -17,11 +17,11 @@ namespace baseline class CodeAnalysis; } -using uint256 = intx::uint256; using bytes = std::basic_string; using bytes_view = std::basic_string_view; /// Provides memory for EVM stack. +template class StackSpace { public: @@ -29,15 +29,14 @@ class StackSpace static constexpr auto limit = 1024; /// Returns the pointer to the "bottom", i.e. below the stack space. - [[nodiscard, clang::no_sanitize("bounds")]] uint256* bottom() noexcept + [[nodiscard, clang::no_sanitize("bounds")]] nil::blueprint::zkevm_word* bottom() noexcept { return m_stack_space - 1; } private: /// The storage allocated for maximum possible number of items. - /// Items are aligned to 256 bits for better packing in cache lines. - alignas(sizeof(uint256)) uint256 m_stack_space[limit]; + nil::blueprint::zkevm_word m_stack_space[limit]; }; @@ -89,10 +88,10 @@ class Memory void grow(size_t new_size) noexcept { // Restriction for future changes. EVM always has memory size as multiple of 32 bytes. - INTX_REQUIRE(new_size % 32 == 0); + assert(new_size % 32 == 0); // Allow only growing memory. Include hint for optimizing compiler. - INTX_REQUIRE(new_size > m_size); + assert(new_size > m_size); if (new_size > m_capacity) { @@ -117,6 +116,7 @@ class Memory /// Generic execution state for generic instructions implementations. // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) +template class ExecutionState { public: @@ -138,7 +138,7 @@ class ExecutionState evmc_status_code status = EVMC_SUCCESS; size_t output_offset = 0; size_t output_size = 0; - std::shared_ptr m_handler; + std::shared_ptr> assigner; private: evmc_tx_context m_tx = {}; @@ -156,24 +156,21 @@ class ExecutionState /// Stack space allocation. /// /// This is the last field to make other fields' offsets of reasonable values. - StackSpace stack_space; + StackSpace stack_space; ExecutionState() noexcept = default; ExecutionState(const evmc_message& message, evmc_revision revision, const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code, - bytes_view _data) noexcept + bytes_view _data, std::shared_ptr> _assigner) noexcept : msg{&message}, host{host_interface, host_ctx}, rev{revision}, original_code{_code}, - data{_data} + data{_data}, + assigner{_assigner} {} - void set_handler(std::shared_ptr handler) { - m_handler = handler; - } - /// Resets the contents of the ExecutionState so that it could be reused. void reset(const evmc_message& message, evmc_revision revision, const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code, @@ -197,7 +194,7 @@ class ExecutionState const evmc_tx_context& get_tx_context() noexcept { - if (INTX_UNLIKELY(m_tx.block_timestamp == 0)) + if (bool(m_tx.block_timestamp == 0)) m_tx = host.get_tx_context(); return m_tx; } diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 2abad954..12ddede1 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -3,12 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include "baseline.hpp" #include "eof.hpp" #include "execution_state.hpp" #include "instructions_traits.hpp" #include "instructions_xmacro.hpp" -#include "fprinter.h" #include #include @@ -18,27 +16,28 @@ using code_iterator = const uint8_t*; /// Represents the pointer to the stack top item /// and allows retrieving stack items and manipulating the pointer. +template class StackTop { - uint256* m_top; + nil::blueprint::zkevm_word* m_top; public: - StackTop(uint256* top) noexcept : m_top{top} {} + StackTop(nil::blueprint::zkevm_word* top) noexcept : m_top{top} {} /// Returns the reference to the stack item by index, where 0 means the top item /// and positive index values the items further down the stack. /// Using [-1] is also valid, but .push() should be used instead. - [[nodiscard]] uint256& operator[](int index) noexcept { return m_top[-index]; } + [[nodiscard]] nil::blueprint::zkevm_word& operator[](int index) noexcept { return m_top[-index]; } /// Returns the reference to the stack top item. - [[nodiscard]] uint256& top() noexcept { return *m_top; } + [[nodiscard]] nil::blueprint::zkevm_word& top() noexcept { return *m_top; } /// Returns the current top item and move the stack top pointer down. /// The value is returned by reference because the stack slot remains valid. - [[nodiscard]] uint256& pop() noexcept { return *m_top--; } + [[nodiscard]] nil::blueprint::zkevm_word& pop() noexcept { return *m_top--; } /// Assigns the value to the stack top and moves the stack top pointer up. - void push(const uint256& value) noexcept { *++m_top = value; } + void push(const nil::blueprint::zkevm_word& value) noexcept { *++m_top = value; } }; @@ -104,1170 +103,1382 @@ inline constexpr int64_t copy_cost(uint64_t size_in_bytes) noexcept return gas_left; } -/// Check memory requirements of a reasonable size. -inline bool check_memory( - int64_t& gas_left, Memory& memory, const uint256& offset, uint64_t size) noexcept +/// The gas cost specification for storage instructions. +struct StorageCostSpec { - // TODO: This should be done in intx. - // There is "branchless" variant of this using | instead of ||, but benchmarks difference - // is within noise. This should be decided when moving the implementation to intx. - if (((offset[3] | offset[2] | offset[1]) != 0) || (offset[0] > max_buffer_size)) - return false; - - const auto new_size = static_cast(offset) + size; - if (new_size > memory.size()) - gas_left = grow_memory(gas_left, memory, new_size); + bool net_cost; ///< Is this net gas cost metering schedule? + int16_t warm_access; ///< Storage warm access cost, YP: G_{warmaccess} + int16_t set; ///< Storage addition cost, YP: G_{sset} + int16_t reset; ///< Storage modification cost, YP: G_{sreset} + int16_t clear; ///< Storage deletion refund, YP: R_{sclear} +}; - return gas_left >= 0; // Always true for no-grow case. -} +/// Table of gas cost specification for storage instructions per EVM revision. +/// TODO: This can be moved to instruction traits and be used in other places: e.g. +/// SLOAD cost, replacement for warm_storage_read_cost. +constexpr auto storage_cost_spec = []() noexcept { + std::array tbl{}; + + // Legacy cost schedule. + for (auto rev : {EVMC_FRONTIER, EVMC_HOMESTEAD, EVMC_TANGERINE_WHISTLE, EVMC_SPURIOUS_DRAGON, + EVMC_BYZANTIUM, EVMC_PETERSBURG}) + tbl[rev] = {false, 200, 20000, 5000, 15000}; + + // Net cost schedule. + tbl[EVMC_CONSTANTINOPLE] = {true, 200, 20000, 5000, 15000}; + tbl[EVMC_ISTANBUL] = {true, 800, 20000, 5000, 15000}; + tbl[EVMC_BERLIN] = { + true, instr::warm_storage_read_cost, 20000, 5000 - instr::cold_sload_cost, 15000}; + tbl[EVMC_LONDON] = { + true, instr::warm_storage_read_cost, 20000, 5000 - instr::cold_sload_cost, 4800}; + tbl[EVMC_PARIS] = tbl[EVMC_LONDON]; + tbl[EVMC_SHANGHAI] = tbl[EVMC_LONDON]; + tbl[EVMC_CANCUN] = tbl[EVMC_LONDON]; + tbl[EVMC_PRAGUE] = tbl[EVMC_LONDON]; + return tbl; +}(); + + +struct StorageStoreCost +{ + int16_t gas_cost; + int16_t gas_refund; +}; -/// Check memory requirements for "copy" instructions. -inline bool check_memory( - int64_t& gas_left, Memory& memory, const uint256& offset, const uint256& size) noexcept -{ - if (size == 0) // Copy of size 0 is always valid (even if offset is huge). - return true; +// The lookup table of SSTORE costs by the storage update status. +constexpr auto sstore_costs = []() noexcept { + std::array, + EVMC_MAX_REVISION + 1> + tbl{}; - // This check has 3 same word checks with the check above. - // However, compilers do decent although not perfect job unifying common instructions. - // TODO: This should be done in intx. - if (((size[3] | size[2] | size[1]) != 0) || (size[0] > max_buffer_size)) - return false; + for (size_t rev = EVMC_FRONTIER; rev <= EVMC_MAX_REVISION; ++rev) + { + auto& e = tbl[rev]; + if (const auto c = storage_cost_spec[rev]; !c.net_cost) // legacy + { + e[EVMC_STORAGE_ADDED] = {c.set, 0}; + e[EVMC_STORAGE_DELETED] = {c.reset, c.clear}; + e[EVMC_STORAGE_MODIFIED] = {c.reset, 0}; + e[EVMC_STORAGE_ASSIGNED] = e[EVMC_STORAGE_MODIFIED]; + e[EVMC_STORAGE_DELETED_ADDED] = e[EVMC_STORAGE_ADDED]; + e[EVMC_STORAGE_MODIFIED_DELETED] = e[EVMC_STORAGE_DELETED]; + e[EVMC_STORAGE_DELETED_RESTORED] = e[EVMC_STORAGE_ADDED]; + e[EVMC_STORAGE_ADDED_DELETED] = e[EVMC_STORAGE_DELETED]; + e[EVMC_STORAGE_MODIFIED_RESTORED] = e[EVMC_STORAGE_MODIFIED]; + } + else // net cost + { + e[EVMC_STORAGE_ASSIGNED] = {c.warm_access, 0}; + e[EVMC_STORAGE_ADDED] = {c.set, 0}; + e[EVMC_STORAGE_DELETED] = {c.reset, c.clear}; + e[EVMC_STORAGE_MODIFIED] = {c.reset, 0}; + e[EVMC_STORAGE_DELETED_ADDED] = {c.warm_access, static_cast(-c.clear)}; + e[EVMC_STORAGE_MODIFIED_DELETED] = {c.warm_access, c.clear}; + e[EVMC_STORAGE_DELETED_RESTORED] = { + c.warm_access, static_cast(c.reset - c.warm_access - c.clear)}; + e[EVMC_STORAGE_ADDED_DELETED] = { + c.warm_access, static_cast(c.set - c.warm_access)}; + e[EVMC_STORAGE_MODIFIED_RESTORED] = { + c.warm_access, static_cast(c.reset - c.warm_access)}; + } + } - return check_memory(gas_left, memory, offset, static_cast(size)); -} + return tbl; +}(); namespace instr::core { +template +struct operation { + /// Check memory requirements of a reasonable size. + static inline bool check_memory( + int64_t& gas_left, Memory& memory, const nil::blueprint::zkevm_word& offset, uint64_t size) noexcept + { + // TODO: This should be done in intx. + // There is "branchless" variant of this using | instead of ||, but benchmarks difference + // is within noise. This should be decided when moving the implementation to intx. + auto offset_uint = offset.to_uint64(); + if (offset_uint > max_buffer_size) + return false; + + const auto new_size = offset_uint + size; + if (new_size > memory.size()) + gas_left = grow_memory(gas_left, memory, new_size); + + return gas_left >= 0; // Always true for no-grow case. + } -/// The "core" instruction implementations. -/// -/// These are minimal EVM instruction implementations which assume: -/// - the stack requirements (overflow, underflow) have already been checked, -/// - the "base" gas const has already been charged, -/// - the `stack` pointer points to the EVM stack top element. -/// Moreover, these implementations _do not_ inform about new stack height -/// after execution. The adjustment must be performed by the caller. -inline void noop(StackTop /*stack*/) noexcept {} -inline constexpr auto pop = noop; -inline constexpr auto jumpdest = noop; - -template -inline TermResult stop_impl( - StackTop /*stack*/, int64_t gas_left, ExecutionState& /*state*/) noexcept -{ - return {Status, gas_left}; -} -inline constexpr auto stop = stop_impl; -inline constexpr auto invalid = stop_impl; + /// Check memory requirements for "copy" instructions. + static inline bool check_memory( + int64_t& gas_left, Memory& memory, const nil::blueprint::zkevm_word& offset, const nil::blueprint::zkevm_word& size) noexcept + { + if (size == 0) // Copy of size 0 is always valid (even if offset is huge). + return true; -inline void add(StackTop stack) noexcept -{ - stack.top() += stack.pop(); -} + // This check has 3 same word checks with the check above. + // However, compilers do decent although not perfect job unifying common instructions. + // TODO: This should be done in intx. + const auto size_uint = size.to_uint64(); + if (size_uint > max_buffer_size) + return false; -inline void mul(StackTop stack, ExecutionState& state) noexcept -{ - state.m_handler->set_witness(static_cast(state.msg->depth), 0, 0, stack[0]); - state.m_handler->set_witness(static_cast(state.msg->depth), 0, 1, stack[1]); - stack.top() *= stack.pop(); -} + return check_memory(gas_left, memory, offset, size_uint); + } + /// The "core" instruction implementations. + /// + /// These are minimal EVM instruction implementations which assume: + /// - the stack requirements (overflow, underflow) have already been checked, + /// - the "base" gas const has already been charged, + /// - the `stack` pointer points to the EVM stack top element. + /// Moreover, these implementations _do not_ inform about new stack height + /// after execution. The adjustment must be performed by the caller. + static inline void noop(StackTop /*stack*/) noexcept {} + static inline void pop(StackTop /*stack*/) noexcept {} + static inline void jumpdest(StackTop /*stack*/) noexcept {} + + static inline TermResult stop_impl( + StackTop /*stack*/, int64_t gas_left, ExecutionState& /*state*/, evmc_status_code Status) noexcept + { + return {Status, gas_left}; + } + static inline TermResult stop(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return stop_impl(stack, gas_left, state, EVMC_SUCCESS); + } -inline void sub(StackTop stack) noexcept -{ - stack[1] = stack[0] - stack[1]; -} + static inline TermResult invalid(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return stop_impl(stack, gas_left, state, EVMC_INVALID_INSTRUCTION); + } -inline void div(StackTop stack) noexcept -{ - auto& v = stack[1]; - v = v != 0 ? stack[0] / v : 0; -} + static inline void add(StackTop stack) noexcept + { + stack.top() = stack.top() + stack.pop(); + } -inline void sdiv(StackTop stack) noexcept -{ - auto& v = stack[1]; - v = v != 0 ? intx::sdivrem(stack[0], v).quot : 0; -} + static inline void mul(StackTop stack, ExecutionState& state) noexcept + { + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 0) = stack[0].to_uint64(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 1) = stack[1].to_uint64(); + stack.top() = stack.top() * stack.pop(); + } -inline void mod(StackTop stack) noexcept -{ - auto& v = stack[1]; - v = v != 0 ? stack[0] % v : 0; -} + static inline void sub(StackTop stack) noexcept + { + stack[1] = stack[0] - stack[1]; + } -inline void smod(StackTop stack) noexcept -{ - auto& v = stack[1]; - v = v != 0 ? intx::sdivrem(stack[0], v).rem : 0; -} + static inline void div(StackTop stack) noexcept + { + auto& v = stack[1]; + v = v != 0 ? stack[0] / v : 0; + } -inline void addmod(StackTop stack) noexcept -{ - const auto& x = stack.pop(); - const auto& y = stack.pop(); - auto& m = stack.top(); - m = m != 0 ? intx::addmod(x, y, m) : 0; -} + static inline void sdiv(StackTop stack) noexcept + { + auto& v = stack[1]; + v = stack[0].sdiv(v); + } -inline void mulmod(StackTop stack) noexcept -{ - const auto& x = stack[0]; - const auto& y = stack[1]; - auto& m = stack[2]; - m = m != 0 ? intx::mulmod(x, y, m) : 0; -} + static inline void mod(StackTop stack) noexcept + { + auto& v = stack[1]; + v = v != 0 ? stack[0] % v : 0; + } -inline Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& base = stack.pop(); - auto& exponent = stack.top(); - - const auto exponent_significant_bytes = - static_cast(intx::count_significant_bytes(exponent)); - const auto exponent_cost = state.rev >= EVMC_SPURIOUS_DRAGON ? 50 : 10; - const auto additional_cost = exponent_significant_bytes * exponent_cost; - if ((gas_left -= additional_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - - exponent = intx::exp(base, exponent); - return {EVMC_SUCCESS, gas_left}; -} + static inline void smod(StackTop stack) noexcept + { + auto& v = stack[1]; + v = stack[0].smod(v); + } -inline void signextend(StackTop stack) noexcept -{ - const auto& ext = stack.pop(); - auto& x = stack.top(); + static inline void addmod(StackTop stack) noexcept + { + const auto& x = stack.pop(); + const auto& y = stack.pop(); + auto& m = stack.top(); + m = x.addmod(y, m); + } - if (ext < 31) // For 31 we also don't need to do anything. + static inline void mulmod(StackTop stack) noexcept { - const auto e = ext[0]; // uint256 -> uint64. - const auto sign_word_index = - static_cast(e / sizeof(e)); // Index of the word with the sign bit. - const auto sign_byte_index = e % sizeof(e); // Index of the sign byte in the sign word. - auto& sign_word = x[sign_word_index]; + const auto& x = stack[0]; + const auto& y = stack[1]; + auto& m = stack[2]; + m = x.mulmod(y, m); + } - const auto sign_byte_offset = sign_byte_index * 8; - const auto sign_byte = sign_word >> sign_byte_offset; // Move sign byte to position 0. + static inline Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& base = stack.pop(); + auto& exponent = stack.top(); - // Sign-extend the "sign" byte and move it to the right position. Value bits are zeros. - const auto sext_byte = static_cast(int64_t{static_cast(sign_byte)}); - const auto sext = sext_byte << sign_byte_offset; + const unsigned exponent_significant_bytes = exponent.count_significant_bytes(); + const unsigned exponent_cost = state.rev >= EVMC_SPURIOUS_DRAGON ? 50 : 10; + const auto additional_cost = exponent_significant_bytes * exponent_cost; + if ((gas_left -= additional_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - const auto sign_mask = ~uint64_t{0} << sign_byte_offset; - const auto value = sign_word & ~sign_mask; // Reset extended bytes. - sign_word = sext | value; // Combine the result word. + exponent = base.exp(exponent); + return {EVMC_SUCCESS, gas_left}; + } - // Produce bits (all zeros or ones) for extended words. This is done by SAR of - // the sign-extended byte. Shift by any value 7-63 would work. - const auto sign_ex = static_cast(static_cast(sext_byte) >> 8); + static inline void signextend(StackTop stack) noexcept + { + const auto& ext = stack.pop(); + auto& x = stack.top(); - for (size_t i = 3; i > sign_word_index; --i) - x[i] = sign_ex; // Clear extended words. - } -} + if (ext < 31) // For 31 we also don't need to do anything. + { + const auto e = ext.to_uint64(); // uint256 -> uint64. + const auto sign_word_index = + static_cast(e / sizeof(e)); // Index of the word with the sign bit. + const auto sign_byte_index = e % sizeof(e); // Index of the sign byte in the sign word. + auto sign_word = x.to_uint64(sign_word_index); -inline void lt(StackTop stack) noexcept -{ - const auto& x = stack.pop(); - stack[0] = x < stack[0]; -} + const auto sign_byte_offset = sign_byte_index * 8; + const auto sign_byte = sign_word >> sign_byte_offset; // Move sign byte to position 0. -inline void gt(StackTop stack) noexcept -{ - const auto& x = stack.pop(); - stack[0] = stack[0] < x; // Arguments are swapped and < is used. -} + // Sign-extend the "sign" byte and move it to the right position. Value bits are zeros. + const auto sext_byte = static_cast(int64_t{static_cast(sign_byte)}); + const auto sext = sext_byte << sign_byte_offset; -inline void slt(StackTop stack) noexcept -{ - const auto& x = stack.pop(); - stack[0] = slt(x, stack[0]); -} + const auto sign_mask = ~uint64_t{0} << sign_byte_offset; + const auto value = sign_word & ~sign_mask; // Reset extended bytes. + sign_word = sext | value; // Combine the result word. -inline void sgt(StackTop stack) noexcept -{ - const auto& x = stack.pop(); - stack[0] = slt(stack[0], x); // Arguments are swapped and SLT is used. -} + // Produce bits (all zeros or ones) for extended words. This is done by SAR of + // the sign-extended byte. Shift by any value 7-63 would work. + const auto sign_ex = static_cast(static_cast(sext_byte) >> 8); -inline void eq(StackTop stack) noexcept -{ - stack[1] = stack[0] == stack[1]; -} + for (size_t i = 3; i > sign_word_index; --i) + x.set_val(sign_ex, i); // Clear extended words. + } + } -inline void iszero(StackTop stack) noexcept -{ - stack.top() = stack.top() == 0; -} + static inline void lt(StackTop stack) noexcept + { + const auto& x = stack.pop(); + stack[0] = x < stack[0]; + } -inline void and_(StackTop stack) noexcept -{ - stack.top() &= stack.pop(); -} + static inline void gt(StackTop stack) noexcept + { + const auto& x = stack.pop(); + stack[0] = stack[0] < x; // Arguments are swapped and < is used. + } -inline void or_(StackTop stack) noexcept -{ - stack.top() |= stack.pop(); -} + static inline void slt(StackTop stack) noexcept + { + const auto& x = stack.pop(); + stack[0] = x.slt(stack[0]); + } -inline void xor_(StackTop stack) noexcept -{ - stack.top() ^= stack.pop(); -} + static inline void sgt(StackTop stack) noexcept + { + const auto& x = stack.pop(); + stack[0] = stack[0].slt(x); // Arguments are swapped and SLT is used. + } -inline void not_(StackTop stack) noexcept -{ - stack.top() = ~stack.top(); -} + static inline void eq(StackTop stack) noexcept + { + stack[1] = stack[0] == stack[1]; + } -inline void byte(StackTop stack) noexcept -{ - const auto& n = stack.pop(); - auto& x = stack.top(); + static inline void iszero(StackTop stack) noexcept + { + stack.top() = stack.top() == 0; + } - const bool n_valid = n < 32; - const uint64_t byte_mask = (n_valid ? 0xff : 0); + static inline void and_(StackTop stack) noexcept + { + stack.top() = stack.top() & stack.pop(); + } - const auto index = 31 - static_cast(n[0] % 32); - const auto word = x[index / 8]; - const auto byte_index = index % 8; - const auto byte = (word >> (byte_index * 8)) & byte_mask; - x = byte; -} + static inline void or_(StackTop stack) noexcept + { + stack.top() = stack.top() | stack.pop(); + } -inline void shl(StackTop stack) noexcept -{ - stack.top() <<= stack.pop(); -} + static inline void xor_(StackTop stack) noexcept + { + stack.top() = stack.top() ^stack.pop(); + } -inline void shr(StackTop stack) noexcept -{ - stack.top() >>= stack.pop(); -} + static inline void not_(StackTop stack) noexcept + { + stack.top() = ~stack.top(); + } -inline void sar(StackTop stack) noexcept -{ - const auto& y = stack.pop(); - auto& x = stack.top(); + static inline void byte(StackTop stack) noexcept + { + const auto& n = stack.pop(); + auto& x = stack.top(); - const bool is_neg = static_cast(x[3]) < 0; // Inspect the top bit (words are LE). - const auto sign_mask = is_neg ? ~uint256{} : uint256{}; + const bool n_valid = n < 32; + const uint64_t byte_mask = (n_valid ? 0xff : 0); - const auto mask_shift = (y < 256) ? (256 - y[0]) : 0; - x = (x >> y) | (sign_mask << mask_shift); -} + const auto index = 31 - static_cast(n.to_uint64() % 32); + const auto word = x.to_uint64(index / 8); + const auto byte_index = index % 8; + const auto byte = (word >> (byte_index * 8)) & byte_mask; + x = nil::blueprint::zkevm_word(byte); + } -inline Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& index = stack.pop(); - auto& size = stack.top(); - - if (!check_memory(gas_left, state.memory, index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; - - const auto i = static_cast(index); - const auto s = static_cast(size); - const auto w = num_words(s); - const auto cost = w * 6; - if ((gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - - auto data = s != 0 ? &state.memory[i] : nullptr; - size = intx::be::load(ethash::keccak256(data, s)); - return {EVMC_SUCCESS, gas_left}; -} + static inline void shl(StackTop stack) noexcept + { + stack.top() = stack.top() << stack.pop(); + } + static inline void shr(StackTop stack) noexcept + { + stack.top() = stack.top() >> stack.pop(); + } -inline void address(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.msg->recipient)); -} + static inline void sar(StackTop stack) noexcept + { + const auto& y = stack.pop(); + auto& x = stack.top(); -inline Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - auto& x = stack.top(); - const auto addr = intx::be::trunc(x); + const bool is_neg = x.to_uint64(3) < 0; // Inspect the top bit (words are LE). + const nil::blueprint::zkevm_word sign_mask = is_neg ? 1 : 0; + + const auto mask_shift = (y < 256) ? (256 - y.to_uint64(0)) : 0; + x = (x >> y) | (sign_mask << mask_shift); + } - if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + static inline Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + const auto& index = stack.pop(); + auto& size = stack.top(); + + if (!check_memory(gas_left, state.memory, index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto i = index.to_uint64(); + const auto s = size.to_uint64(); + const auto w = num_words(s); + const auto cost = w * 6; + if ((gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; + + auto data = s != 0 ? &state.memory[i] : nullptr; + size = nil::blueprint::zkevm_word(ethash::keccak256(data, s)); + return {EVMC_SUCCESS, gas_left}; } - x = intx::be::load(state.host.get_balance(addr)); - return {EVMC_SUCCESS, gas_left}; -} -inline void origin(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().tx_origin)); -} + static inline void address(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.msg->recipient)); + } -inline void caller(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.msg->sender)); -} + static inline Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + auto& x = stack.top(); + const auto addr = x.to_address(); -inline void callvalue(StackTop stack, ExecutionState& state) noexcept -{ - auto val = intx::be::load(state.msg->value); - state.m_handler->set_witness(static_cast(state.msg->depth), 1, 0, val); - stack.push(val); -} + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } -inline void calldataload(StackTop stack, ExecutionState& state) noexcept -{ - auto& index = stack.top(); - state.m_handler->set_witness(static_cast(state.msg->depth), 1, 1, index); + x = nil::blueprint::zkevm_word(state.host.get_balance(addr)); + return {EVMC_SUCCESS, gas_left}; + } - if (state.msg->input_size < index) - index = 0; - else + static inline void origin(StackTop stack, ExecutionState& state) noexcept { - const auto begin = static_cast(index); - const auto end = std::min(begin + 32, state.msg->input_size); + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_origin)); + } - uint8_t data[32] = {}; - for (size_t i = 0; i < (end - begin); ++i) - data[i] = state.msg->input_data[begin + i]; + static inline void caller(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.msg->sender)); + } - index = intx::be::load(data); + static inline void callvalue(StackTop stack, ExecutionState& state) noexcept + { + auto val = nil::blueprint::zkevm_word(state.msg->value); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 0) = val.to_uint64(); + stack.push(val); } -} -inline void calldatasize(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(state.msg->input_size); -} + static inline void calldataload(StackTop stack, ExecutionState& state) noexcept + { + auto& index = stack.top(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 1) = index.to_uint64(); -inline Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& mem_index = stack.pop(); - const auto& input_index = stack.pop(); - const auto& size = stack.pop(); + const auto index_uint64 = index.to_uint64(); + if (state.msg->input_size < index_uint64) + index = 0; + else + { + const auto begin = index_uint64; + const auto end = std::min(begin + 32, state.msg->input_size); - if (!check_memory(gas_left, state.memory, mem_index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + uint8_t data[32] = {}; + for (size_t i = 0; i < (end - begin); ++i) + data[i] = state.msg->input_data[begin + i]; - auto dst = static_cast(mem_index); - auto src = state.msg->input_size < input_index ? state.msg->input_size : - static_cast(input_index); - auto s = static_cast(size); - auto copy_size = std::min(s, state.msg->input_size - src); + index = nil::blueprint::zkevm_word(data, 32); + } + } - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + static inline void calldatasize(StackTop stack, ExecutionState& state) noexcept + { + stack.push(state.msg->input_size); + } - if (copy_size > 0) - std::memcpy(&state.memory[dst], &state.msg->input_data[src], copy_size); + static inline Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& mem_index = stack.pop(); + const auto& input_index = stack.pop(); + const auto& size = stack.pop(); - if (s - copy_size > 0) - std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - return {EVMC_SUCCESS, gas_left}; -} + auto dst = mem_index.to_uint64(); + const auto input_index_uint64 = input_index.to_uint64(); + auto src = state.msg->input_size < input_index_uint64 ? state.msg->input_size : input_index_uint64; + auto s = size.to_uint64(); + auto copy_size = std::min(s, state.msg->input_size - src); -inline void codesize(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(state.original_code.size()); -} + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; -inline Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - // TODO: Similar to calldatacopy(). + if (copy_size > 0) + std::memcpy(&state.memory[dst], &state.msg->input_data[src], copy_size); - const auto& mem_index = stack.pop(); - const auto& input_index = stack.pop(); - const auto& size = stack.pop(); + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); - if (!check_memory(gas_left, state.memory, mem_index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + return {EVMC_SUCCESS, gas_left}; + } - const auto code_size = state.original_code.size(); - const auto dst = static_cast(mem_index); - const auto src = code_size < input_index ? code_size : static_cast(input_index); - const auto s = static_cast(size); - const auto copy_size = std::min(s, code_size - src); + static inline void codesize(StackTop stack, ExecutionState& state) noexcept + { + stack.push(state.original_code.size()); + } - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + static inline Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + // TODO: Similar to calldatacopy(). - // TODO: Add unit tests for each combination of conditions. - if (copy_size > 0) - std::memcpy(&state.memory[dst], &state.original_code[src], copy_size); + const auto& mem_index = stack.pop(); + const auto& input_index = stack.pop(); + const auto& size = stack.pop(); - if (s - copy_size > 0) - std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - return {EVMC_SUCCESS, gas_left}; -} + const auto code_size = state.original_code.size(); + const auto dst = mem_index.to_uint64(); + const auto input_index_uint64 = input_index.to_uint64(); + const auto src = code_size < input_index_uint64 ? code_size : input_index_uint64; + const auto s = size.to_uint64(); + const auto copy_size = std::min(s, code_size - src); + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; -inline void gasprice(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().tx_gas_price)); -} + // TODO: Add unit tests for each combination of conditions. + if (copy_size > 0) + std::memcpy(&state.memory[dst], &state.original_code[src], copy_size); -inline void basefee(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().block_base_fee)); -} + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); -inline void blobhash(StackTop stack, ExecutionState& state) noexcept -{ - auto& index = stack.top(); - const auto& tx = state.get_tx_context(); + return {EVMC_SUCCESS, gas_left}; + } - index = (index < tx.blob_hashes_count) ? - intx::be::load(tx.blob_hashes[static_cast(index)]) : - 0; -} -inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().blob_base_fee)); -} + static inline void gasprice(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_gas_price)); + } -inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - auto& x = stack.top(); - const auto addr = intx::be::trunc(x); + static inline void basefee(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_base_fee)); + } - if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + static inline void blobhash(StackTop stack, ExecutionState& state) noexcept { - if ((gas_left -= instr::additional_cold_account_access_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + auto& index = stack.top(); + const auto& tx = state.get_tx_context(); + const auto index_uin64 = index.to_uint64(); + + index = (index_uin64 < tx.blob_hashes_count) ? + nil::blueprint::zkevm_word(tx.blob_hashes[index_uin64]) : + 0; } - x = state.host.get_code_size(addr); - return {EVMC_SUCCESS, gas_left}; -} + static inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().blob_base_fee)); + } -inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto addr = intx::be::trunc(stack.pop()); - const auto& mem_index = stack.pop(); - const auto& input_index = stack.pop(); - const auto& size = stack.pop(); + static inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + auto& x = stack.top(); + const auto addr = x.to_address(); - if (!check_memory(gas_left, state.memory, mem_index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } - const auto s = static_cast(size); - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + x = state.host.get_code_size(addr); + return {EVMC_SUCCESS, gas_left}; + } - if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + static inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + const auto addr = stack.pop().to_address(); + const auto& mem_index = stack.pop(); + const auto& input_index = stack.pop(); + const auto& size = stack.pop(); + + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; + + const auto s = size.to_uint64(); + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; + + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } + + if (s > 0) + { + const auto input_index_uint64 = input_index.to_uint64(); + const auto src = + (max_buffer_size < input_index_uint64) ? max_buffer_size : input_index_uint64; + const auto dst = mem_index.to_uint64(); + const auto num_bytes_copied = state.host.copy_code(addr, src, &state.memory[dst], s); + if (const auto num_bytes_to_clear = s - num_bytes_copied; num_bytes_to_clear > 0) + std::memset(&state.memory[dst + num_bytes_copied], 0, num_bytes_to_clear); + } + + return {EVMC_SUCCESS, gas_left}; } - if (s > 0) + static inline void returndatasize(StackTop stack, ExecutionState& state) noexcept { - const auto src = - (max_buffer_size < input_index) ? max_buffer_size : static_cast(input_index); - const auto dst = static_cast(mem_index); - const auto num_bytes_copied = state.host.copy_code(addr, src, &state.memory[dst], s); - if (const auto num_bytes_to_clear = s - num_bytes_copied; num_bytes_to_clear > 0) - std::memset(&state.memory[dst + num_bytes_copied], 0, num_bytes_to_clear); + stack.push(state.return_data.size()); } - return {EVMC_SUCCESS, gas_left}; -} - -inline void returndatasize(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(state.return_data.size()); -} + static inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& mem_index = stack.pop(); + const auto& input_index = stack.pop(); + const auto& size = stack.pop(); -inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& mem_index = stack.pop(); - const auto& input_index = stack.pop(); - const auto& size = stack.pop(); + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - if (!check_memory(gas_left, state.memory, mem_index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + auto dst = mem_index.to_uint64(); + auto s = size.to_uint64(); - auto dst = static_cast(mem_index); - auto s = static_cast(size); + auto src = input_index.to_uint64(); + if (state.return_data.size() < src) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; - if (state.return_data.size() < input_index) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; - auto src = static_cast(input_index); + if (src + s > state.return_data.size()) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; - if (src + s > state.return_data.size()) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + if (s > 0) + std::memcpy(&state.memory[dst], &state.return_data[src], s); - if (s > 0) - std::memcpy(&state.memory[dst], &state.return_data[src], s); + return {EVMC_SUCCESS, gas_left}; + } - return {EVMC_SUCCESS, gas_left}; -} + static inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + auto& x = stack.top(); + const auto addr = x.to_address(); -inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - auto& x = stack.top(); - const auto addr = intx::be::trunc(x); + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } - if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) - { - if ((gas_left -= instr::additional_cold_account_access_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + x = nil::blueprint::zkevm_word(state.host.get_code_hash(addr)); + return {EVMC_SUCCESS, gas_left}; } - x = intx::be::load(state.host.get_code_hash(addr)); - return {EVMC_SUCCESS, gas_left}; -} - -inline void blockhash(StackTop stack, ExecutionState& state) noexcept -{ - auto& number = stack.top(); - - const auto upper_bound = state.get_tx_context().block_number; - const auto lower_bound = std::max(upper_bound - 256, decltype(upper_bound){0}); - const auto n = static_cast(number); - const auto header = - (number < upper_bound && n >= lower_bound) ? state.host.get_block_hash(n) : evmc::bytes32{}; - number = intx::be::load(header); -} + static inline void blockhash(StackTop stack, ExecutionState& state) noexcept + { + auto& number = stack.top(); + + const auto upper_bound = state.get_tx_context().block_number; + const auto lower_bound = std::max(upper_bound - 256, decltype(upper_bound){0}); + const auto n = number.to_uint64(); + const auto header = + (decltype(upper_bound)(n) < upper_bound && decltype(upper_bound)(n) >= lower_bound) ? + state.host.get_block_hash(decltype(upper_bound)(n)) : evmc::bytes32{}; + number = nil::blueprint::zkevm_word(header); + } -inline void coinbase(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().block_coinbase)); -} + static inline void coinbase(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_coinbase)); + } -inline void timestamp(StackTop stack, ExecutionState& state) noexcept -{ - // TODO: Add tests for negative timestamp? - stack.push(static_cast(state.get_tx_context().block_timestamp)); -} + static inline void timestamp(StackTop stack, ExecutionState& state) noexcept + { + // TODO: Add tests for negative timestamp? + stack.push(static_cast(state.get_tx_context().block_timestamp)); + } -inline void number(StackTop stack, ExecutionState& state) noexcept -{ - // TODO: Add tests for negative block number? - stack.push(static_cast(state.get_tx_context().block_number)); -} + static inline void number(StackTop stack, ExecutionState& state) noexcept + { + // TODO: Add tests for negative block number? + stack.push(static_cast(state.get_tx_context().block_number)); + } -inline void prevrandao(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().block_prev_randao)); -} + static inline void prevrandao(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_prev_randao)); + } -inline void gaslimit(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(static_cast(state.get_tx_context().block_gas_limit)); -} + static inline void gaslimit(StackTop stack, ExecutionState& state) noexcept + { + stack.push(static_cast(state.get_tx_context().block_gas_limit)); + } -inline void chainid(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(intx::be::load(state.get_tx_context().chain_id)); -} + static inline void chainid(StackTop stack, ExecutionState& state) noexcept + { + stack.push(nil::blueprint::zkevm_word(state.get_tx_context().chain_id)); + } -inline void selfbalance(StackTop stack, ExecutionState& state) noexcept -{ - // TODO: introduce selfbalance in EVMC? - stack.push(intx::be::load(state.host.get_balance(state.msg->recipient))); -} + static inline void selfbalance(StackTop stack, ExecutionState& state) noexcept + { + // TODO: introduce selfbalance in EVMC? + stack.push(nil::blueprint::zkevm_word(state.host.get_balance(state.msg->recipient))); + } -template -inline Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - auto& index = stack.top(); + template + static inline Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + auto& index = stack.top(); - if (!check_memory(gas_left, state.memory, index, 32)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (!check_memory(gas_left, state.memory, index, (BlueprintFieldType::modulus_bits / 8))) + return {EVMC_OUT_OF_GAS, gas_left}; - index = intx::be::unsafe::load(&state.memory[static_cast(index)]); - state.m_handler->set_witness(static_cast(state.msg->depth), 2, 2, index); - return {EVMC_SUCCESS, gas_left}; -} + index = nil::blueprint::zkevm_word(&state.memory[index.to_uint64()], (BlueprintFieldType::modulus_bits / 8)); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 2) = index.to_uint64(); + return {EVMC_SUCCESS, gas_left}; + } -template -inline Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& index = stack.pop(); - const auto& value = stack.pop(); + template + static inline Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& index = stack.pop(); + auto& value = stack.pop(); - state.m_handler->set_witness(static_cast(state.msg->depth), 2, 0, value); - state.m_handler->set_witness(static_cast(state.msg->depth), 2, 1, index); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 0) = value.to_uint64(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 1) = index.to_uint64(); - if (!check_memory(gas_left, state.memory, index, 32)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (!check_memory(gas_left, state.memory, index, (BlueprintFieldType::modulus_bits / 8))) + return {EVMC_OUT_OF_GAS, gas_left}; - intx::be::unsafe::store(&state.memory[static_cast(index)], static_cast(value)); - return {EVMC_SUCCESS, gas_left}; -} + value.template store(&state.memory[index.to_uint64()]); + return {EVMC_SUCCESS, gas_left}; + } -inline Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& index = stack.pop(); - const auto& value = stack.pop(); + static inline Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& index = stack.pop(); + const auto& value = stack.pop(); - if (!check_memory(gas_left, state.memory, index, 1)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (!check_memory(gas_left, state.memory, index, 1)) + return {EVMC_OUT_OF_GAS, gas_left}; - state.memory[static_cast(index)] = static_cast(value); - return {EVMC_SUCCESS, gas_left}; -} + state.memory[(int)index.to_uint64()] = value.to_uint64(); + return {EVMC_SUCCESS, gas_left}; + } -Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; + /// Internal jump implementation for JUMP/JUMPI instructions. + static inline code_iterator jump_impl(ExecutionState& state, const nil::blueprint::zkevm_word& dst) noexcept + { + const auto& jumpdest_map = state.analysis.baseline->jumpdest_map; + const auto dst_uint64 = dst.to_uint64(); + if (dst_uint64 >= jumpdest_map.size() || !jumpdest_map[dst_uint64]) + { + state.status = EVMC_BAD_JUMP_DESTINATION; + return nullptr; + } -Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; + return &state.analysis.baseline->executable_code[dst_uint64]; + } -/// Internal jump implementation for JUMP/JUMPI instructions. -inline code_iterator jump_impl(ExecutionState& state, const uint256& dst) noexcept -{ - const auto& jumpdest_map = state.analysis.baseline->jumpdest_map; - if (dst >= jumpdest_map.size() || !jumpdest_map[static_cast(dst)]) + /// JUMP instruction implementation using baseline::CodeAnalysis. + static inline code_iterator jump(StackTop stack, ExecutionState& state, code_iterator /*pos*/) noexcept { - state.status = EVMC_BAD_JUMP_DESTINATION; - return nullptr; + return jump_impl(state, stack.pop()); } - return &state.analysis.baseline->executable_code[static_cast(dst)]; -} + /// JUMPI instruction implementation using baseline::CodeAnalysis. + static inline code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + { + const auto& dst = stack.pop(); + const auto& cond = stack.pop(); + return cond.to_uint64() > 0 ? jump_impl(state, dst) : pos + 1; + } -/// JUMP instruction implementation using baseline::CodeAnalysis. -inline code_iterator jump(StackTop stack, ExecutionState& state, code_iterator /*pos*/) noexcept -{ - return jump_impl(state, stack.pop()); -} + static inline code_iterator rjump(StackTop /*stack*/, ExecutionState& /*state*/, code_iterator pc) noexcept + { + // Reading next 2 bytes is guaranteed to be safe by deploy-time validation. + const auto offset = read_int16_be(&pc[1]); + return pc + 3 + offset; // PC_post_rjump + offset + } -/// JUMPI instruction implementation using baseline::CodeAnalysis. -inline code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto& dst = stack.pop(); - const auto& cond = stack.pop(); - return cond ? jump_impl(state, dst) : pos + 1; -} + static inline code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept + { + const auto cond = stack.pop(); + return cond.to_uint64() > 0 ? rjump(stack, state, pc) : pc + 3; + } -inline code_iterator rjump(StackTop /*stack*/, ExecutionState& /*state*/, code_iterator pc) noexcept -{ - // Reading next 2 bytes is guaranteed to be safe by deploy-time validation. - const auto offset = read_int16_be(&pc[1]); - return pc + 3 + offset; // PC_post_rjump + offset -} + static inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept + { + constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); + const auto case_ = stack.pop(); -inline code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept -{ - const auto cond = stack.pop(); - return cond ? rjump(stack, state, pc) : pc + 3; -} + const auto max_index = pc[1]; + const auto pc_post = pc + 1 + 1 /* max_index */ + (max_index + 1) * REL_OFFSET_SIZE /* tbl */; -inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept -{ - constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); - const auto case_ = stack.pop(); + if (case_.to_uint64() > max_index) + { + return pc_post; + } + else + { + const auto rel_offset = + read_int16_be(&pc[2 + static_cast(case_.to_uint64()) * REL_OFFSET_SIZE]); - const auto max_index = pc[1]; - const auto pc_post = pc + 1 + 1 /* max_index */ + (max_index + 1) * REL_OFFSET_SIZE /* tbl */; + return pc_post + rel_offset; + } + } - if (case_ > max_index) + static inline code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { - return pc_post; + stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); + return pos + 1; } - else + + static inline void msize(StackTop stack, ExecutionState& state) noexcept { - const auto rel_offset = - read_int16_be(&pc[2 + static_cast(case_) * REL_OFFSET_SIZE]); + stack.push(state.memory.size()); + } - return pc_post + rel_offset; + static inline Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept + { + stack.push(gas_left); + return {EVMC_SUCCESS, gas_left}; } -} -inline code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); - return pos + 1; -} + static inline void tload(StackTop stack, ExecutionState& state) noexcept + { + auto& x = stack.top(); + evmc::bytes32 key = x.to_uint256be(); + const auto value = state.host.get_transient_storage(state.msg->recipient, key); + x = nil::blueprint::zkevm_word(value); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 2) = x.to_uint64(); + } -inline void msize(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(state.memory.size()); -} + static inline Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, 0}; + + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 0) = stack[1].to_uint64(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 1) = stack[0].to_uint64(); + evmc::bytes32 key = stack.pop().to_uint256be(); + evmc::bytes32 value = stack.pop().to_uint256be(); + state.host.set_transient_storage(state.msg->recipient, key, value); + return {EVMC_SUCCESS, gas_left}; + } -inline Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept -{ - stack.push(gas_left); - return {EVMC_SUCCESS, gas_left}; -} + static inline void push0(StackTop stack) noexcept + { + stack.push({}); + } -inline void tload(StackTop stack, ExecutionState& state) noexcept -{ - auto& x = stack.top(); - const auto key = intx::be::store(x); - const auto value = state.host.get_transient_storage(state.msg->recipient, key); - x = intx::be::load(value); - state.m_handler->set_witness(static_cast(state.msg->depth), 4, 2, x); -} + /// PUSH instruction implementation. + /// @tparam Len The number of push data bytes, e.g. PUSH3 is push<3>. + /// + /// It assumes that at lest 32 bytes of data are available so code padding is required. + template + static inline code_iterator push(StackTop stack, ExecutionState& /*state*/, code_iterator pos) noexcept + { + // TODO size of field in bytes + constexpr size_t word_size = 8; + constexpr auto num_full_words = Len / word_size; + constexpr auto num_partial_bytes = Len % word_size; + auto data = pos + 1; -inline Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, 0}; - - state.m_handler->set_witness(static_cast(state.msg->depth), 4, 0, stack[1]); - state.m_handler->set_witness(static_cast(state.msg->depth), 4, 1, stack[0]); - const auto key = intx::be::store(stack.pop()); - const auto value = intx::be::store(stack.pop()); - state.host.set_transient_storage(state.msg->recipient, key, value); - return {EVMC_SUCCESS, gas_left}; -} + stack.push(0); + auto& r = stack.top(); -inline void push0(StackTop stack) noexcept -{ - stack.push({}); -} + // Load top partial word. + if constexpr (num_partial_bytes != 0) + { + r.load_partial_data(data, num_partial_bytes, num_full_words); + data += num_partial_bytes; + } + // Load full words. + for (size_t i = 0; i < num_full_words; ++i) + { + r.load_partial_data(data, word_size, num_full_words - 1 - i); + data += word_size; + } -template -inline uint64_t load_partial_push_data(code_iterator pos) noexcept -{ - static_assert(Len > 4 && Len < 8); + return pos + (Len + 1); + } - // It loads up to 3 additional bytes. - return intx::be::unsafe::load(pos) >> (8 * (sizeof(uint64_t) - Len)); -} + /// DUP instruction implementation. + /// @tparam N The number as in the instruction definition, e.g. DUP3 is dup<3>. + template + static inline void dup(StackTop stack) noexcept + { + static_assert(N >= 0 && N <= 16); + if constexpr (N == 0) + { + const auto index = stack.pop(); + assert(index.to_uint64() < std::numeric_limits::max()); + stack.push(stack[(int)index.to_uint64() - 1]); + } + else + { + stack.push(stack[N - 1]); + } + } -template <> -inline uint64_t load_partial_push_data<1>(code_iterator pos) noexcept -{ - return pos[0]; -} + /// SWAP instruction implementation. + /// @tparam N The number as in the instruction definition, e.g. SWAP3 is swap<3>. + template + static inline void swap(StackTop stack) noexcept + { + static_assert(N >= 0 && N <= 16); -template <> -inline uint64_t load_partial_push_data<2>(code_iterator pos) noexcept -{ - return intx::be::unsafe::load(pos); -} + // The simple std::swap(stack.top(), stack[N]) is not used to workaround + // clang missed optimization: https://github.com/llvm/llvm-project/issues/59116 + // TODO(clang): Check if #59116 bug fix has been released. + nil::blueprint::zkevm_word* a; + if constexpr (N == 0) + { + auto& index = stack.pop(); + assert(index < std::numeric_limits::max()); + a = &stack[(int)index.to_uint64()]; + } + else + { + a = &stack[N]; + } + auto& t = stack.top(); + auto t0 = t.to_uint64(0); + auto t1 = t.to_uint64(1); + auto t2 = t.to_uint64(2); + auto t3 = t.to_uint64(3); + t = *a; + a->set_val(t0, 0); + a->set_val(t1, 1); + a->set_val(t2, 2); + a->set_val(t3, 3); + } -template <> -inline uint64_t load_partial_push_data<3>(code_iterator pos) noexcept -{ - // It loads 1 additional byte. - return intx::be::unsafe::load(pos) >> 8; -} + static inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + { + const auto n = pos[1] + 1; -template <> -inline uint64_t load_partial_push_data<4>(code_iterator pos) noexcept -{ - return intx::be::unsafe::load(pos); -} + const auto stack_size = &stack.top() - state.stack_space.bottom(); -/// PUSH instruction implementation. -/// @tparam Len The number of push data bytes, e.g. PUSH3 is push<3>. -/// -/// It assumes that at lest 32 bytes of data are available so code padding is required. -template -inline code_iterator push(StackTop stack, ExecutionState& /*state*/, code_iterator pos) noexcept -{ - constexpr auto num_full_words = Len / sizeof(uint64_t); - constexpr auto num_partial_bytes = Len % sizeof(uint64_t); - auto data = pos + 1; + if (stack_size < n) + { + state.status = EVMC_STACK_UNDERFLOW; + return nullptr; + } - stack.push(0); - auto& r = stack.top(); + stack.push(stack[n - 1]); - // Load top partial word. - if constexpr (num_partial_bytes != 0) - { - r[num_full_words] = load_partial_push_data(data); - data += num_partial_bytes; + return pos + 2; } - // Load full words. - for (size_t i = 0; i < num_full_words; ++i) + static inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { - r[num_full_words - 1 - i] = intx::be::unsafe::load(data); - data += sizeof(uint64_t); - } + const auto n = pos[1] + 1; - return pos + (Len + 1); -} + const auto stack_size = &stack.top() - state.stack_space.bottom(); -/// DUP instruction implementation. -/// @tparam N The number as in the instruction definition, e.g. DUP3 is dup<3>. -template -inline void dup(StackTop stack) noexcept -{ - static_assert(N >= 0 && N <= 16); - if constexpr (N == 0) - { - auto& index = stack.pop(); - assert(index < std::numeric_limits::max()); - stack.push(stack[(int)index - 1]); + if (stack_size <= n) + { + state.status = EVMC_STACK_UNDERFLOW; + return nullptr; + } + + // TODO: This may not be optimal, see instr::core::swap(). + std::swap(stack.top(), stack[n]); + + return pos + 2; } - else + + static inline Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - stack.push(stack[N - 1]); - } -} + const auto& dst_u256 = stack.pop(); + const auto& src_u256 = stack.pop(); + const auto& size_u256 = stack.pop(); -/// SWAP instruction implementation. -/// @tparam N The number as in the instruction definition, e.g. SWAP3 is swap<3>. -template -inline void swap(StackTop stack) noexcept -{ - static_assert(N >= 0 && N <= 16); - - // The simple std::swap(stack.top(), stack[N]) is not used to workaround - // clang missed optimization: https://github.com/llvm/llvm-project/issues/59116 - // TODO(clang): Check if #59116 bug fix has been released. - uint256* a; - if constexpr (N == 0) - { - auto& index = stack.pop(); - assert(index < std::numeric_limits::max()); - a = &stack[(int)index]; - } - else - { - a = &stack[N]; - } - auto& t = stack.top(); - auto t0 = t[0]; - auto t1 = t[1]; - auto t2 = t[2]; - auto t3 = t[3]; - t = *a; - (*a)[0] = t0; - (*a)[1] = t1; - (*a)[2] = t2; - (*a)[3] = t3; -} + if (!check_memory(gas_left, state.memory, std::max(dst_u256, src_u256), size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; -inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto n = pos[1] + 1; + const auto dst = dst_u256.to_uint64(); + const auto src = src_u256.to_uint64(); + const auto size = size_u256.to_uint64(); - const auto stack_size = &stack.top() - state.stack_space.bottom(); + if (const auto cost = copy_cost(size); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - if (stack_size < n) - { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; + if (size > 0) + std::memmove(&state.memory[dst], &state.memory[src], size); + + return {EVMC_SUCCESS, gas_left}; } - stack.push(stack[n - 1]); + static inline void dataload(StackTop stack, ExecutionState& state) noexcept + { + auto& index = stack.top(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 2) = index.to_uint64(); - return pos + 2; -} + if (state.data.size() < index.to_uint64()) + index = 0; + else + { + const auto begin = index.to_uint64(); + const auto end = std::min(begin + 32, state.data.size()); -inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto n = pos[1] + 1; + uint8_t data[32] = {}; + for (size_t i = 0; i < (end - begin); ++i) + data[i] = state.data[begin + i]; - const auto stack_size = &stack.top() - state.stack_space.bottom(); + index = nil::blueprint::zkevm_word(data, (end - begin)); + } + } - if (stack_size <= n) + static inline void datasize(StackTop stack, ExecutionState& state) noexcept { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; + stack.push(state.data.size()); } - // TODO: This may not be optimal, see instr::core::swap(). - std::swap(stack.top(), stack[n]); + static inline code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + { + const auto index = read_uint16_be(&pos[1]); - return pos + 2; -} + stack.push(nil::blueprint::zkevm_word(&state.data[index], (BlueprintFieldType::modulus_bits / 8))); + return pos + 3; + } -inline Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& dst_u256 = stack.pop(); - const auto& src_u256 = stack.pop(); - const auto& size_u256 = stack.pop(); + static inline Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + const auto& mem_index = stack.pop(); + const auto& data_index = stack.pop(); + const auto& size = stack.pop(); - if (!check_memory(gas_left, state.memory, std::max(dst_u256, src_u256), size_u256)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - const auto dst = static_cast(dst_u256); - const auto src = static_cast(src_u256); - const auto size = static_cast(size_u256); + const auto dst = mem_index.to_uint64(); + // TODO why? + const auto data_index_uint64 = data_index.to_uint64(); + const auto src = + state.data.size() < data_index_uint64 ? state.data.size() : data_index_uint64; + const auto s = size.to_uint64(); + const auto copy_size = std::min(s, state.data.size() - src); - if (const auto cost = copy_cost(size); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - if (size > 0) - std::memmove(&state.memory[dst], &state.memory[src], size); + if (copy_size > 0) + std::memcpy(&state.memory[dst], &state.data[src], copy_size); - return {EVMC_SUCCESS, gas_left}; -} + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); -inline void dataload(StackTop stack, ExecutionState& state) noexcept -{ - auto& index = stack.top(); - state.m_handler->set_witness(static_cast(state.msg->depth), 1, 2, index); + return {EVMC_SUCCESS, gas_left}; + } - if (state.data.size() < index) - index = 0; - else + static inline Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state, Opcode Op) noexcept { - const auto begin = static_cast(index); - const auto end = std::min(begin + 32, state.data.size()); + assert(Op == OP_CALL || Op == OP_CALLCODE || Op == OP_DELEGATECALL || Op == OP_STATICCALL); - uint8_t data[32] = {}; - for (size_t i = 0; i < (end - begin); ++i) - data[i] = state.data[begin + i]; + const auto gas = stack.pop(); + const auto dst = stack.pop().to_address(); + const auto value = (Op == OP_STATICCALL || Op == OP_DELEGATECALL) ? 0 : stack.pop(); + const auto has_value = value != 0; + const auto input_offset_u256 = stack.pop(); + const auto input_size_u256 = stack.pop(); + const auto output_offset_u256 = stack.pop(); + const auto output_size_u256 = stack.pop(); - index = intx::be::unsafe::load(data); - } -} - -inline void datasize(StackTop stack, ExecutionState& state) noexcept -{ - stack.push(state.data.size()); -} + stack.push(0); // Assume failure. + state.return_data.clear(); -inline code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto index = read_uint16_be(&pos[1]); + if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } - stack.push(intx::be::unsafe::load(&state.data[index])); - return pos + 3; -} + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; -inline Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& mem_index = stack.pop(); - const auto& data_index = stack.pop(); - const auto& size = stack.pop(); + if (!check_memory(gas_left, state.memory, output_offset_u256, output_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; - if (!check_memory(gas_left, state.memory, mem_index, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + const auto input_offset = input_offset_u256.to_uint64(); + const auto input_size = input_size_u256.to_uint64(); + const auto output_offset = output_offset_u256.to_uint64(); + const auto output_size = output_size_u256.to_uint64(); + + auto msg = evmc_message{}; + msg.kind = (Op == OP_DELEGATECALL) ? EVMC_DELEGATECALL : + (Op == OP_CALLCODE) ? EVMC_CALLCODE : + EVMC_CALL; + msg.flags = (Op == OP_STATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; + msg.depth = state.msg->depth + 1; + msg.recipient = (Op == OP_CALL || Op == OP_STATICCALL) ? dst : state.msg->recipient; + msg.code_address = dst; + msg.sender = (Op == OP_DELEGATECALL) ? state.msg->sender : state.msg->recipient; + msg.value = + (Op == OP_DELEGATECALL) ? state.msg->value : value.to_uint256be(); + + if (input_size > 0) + { + // input_offset may be garbage if input_size == 0. + msg.input_data = &state.memory[input_offset]; + msg.input_size = input_size; + } - const auto dst = static_cast(mem_index); - // TODO why? - const auto src = - state.data.size() < data_index ? state.data.size() : static_cast(data_index); - const auto s = static_cast(size); - const auto copy_size = std::min(s, state.data.size() - src); + auto cost = has_value ? 9000 : 0; - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + if (Op == OP_CALL) + { + if (has_value && state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - if (copy_size > 0) - std::memcpy(&state.memory[dst], &state.data[src], copy_size); + if ((has_value || state.rev < EVMC_SPURIOUS_DRAGON) && !state.host.account_exists(dst)) + cost += 25000; + } - if (s - copy_size > 0) - std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + if ((gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - return {EVMC_SUCCESS, gas_left}; -} + msg.gas = std::numeric_limits::max(); + const auto gas_int64 = (int64_t)gas.to_uint64(); + if (gas_int64 < msg.gas) + msg.gas = gas_int64; -template -inline Result log(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - static_assert(NumTopics <= 4); + if (state.rev >= EVMC_TANGERINE_WHISTLE) // TODO: Always true for STATICCALL. + msg.gas = std::min(msg.gas, gas_left - gas_left / 64); + else if (msg.gas > gas_left) + return {EVMC_OUT_OF_GAS, gas_left}; - if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, 0}; + if (has_value) + { + msg.gas += 2300; // Add stipend. + gas_left += 2300; + } - const auto& offset = stack.pop(); - const auto& size = stack.pop(); + if (state.msg->depth >= 1024) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. - if (!check_memory(gas_left, state.memory, offset, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + if (has_value && nil::blueprint::zkevm_word(state.host.get_balance(state.msg->recipient)) < value) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. - const auto o = static_cast(offset); - const auto s = static_cast(size); + if (Op == OP_DELEGATECALL) + { + if (state.rev >= EVMC_PRAGUE && is_eof_container(state.original_code)) + { + // The code targeted by DELEGATECALL must also be an EOF. + // This restriction has been added to EIP-3540 in + // https://github.com/ethereum/EIPs/pull/7131 + uint8_t target_code_prefix[2]; + const auto s = state.host.copy_code( + msg.code_address, 0, target_code_prefix, std::size(target_code_prefix)); + if (!is_eof_container({target_code_prefix, s})) + return {EVMC_SUCCESS, gas_left}; + } + } - const auto cost = int64_t(s) * 8; - if ((gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + const auto result = state.host.call(msg); + state.return_data.assign(result.output_data, result.output_size); + stack.top() = result.status_code == EVMC_SUCCESS; - std::array topics; // NOLINT(cppcoreguidelines-pro-type-member-init) - for (auto& topic : topics) - topic = intx::be::store(stack.pop()); + if (const auto copy_size = std::min(output_size, result.output_size); copy_size > 0) + std::memcpy(&state.memory[output_offset], result.output_data, copy_size); - const auto data = s != 0 ? &state.memory[o] : nullptr; - state.host.emit_log(state.msg->recipient, data, s, topics.data(), NumTopics); - return {EVMC_SUCCESS, gas_left}; -} + const auto gas_used = msg.gas - result.gas_left; + gas_left -= gas_used; + state.gas_refund += result.gas_refund; + return {EVMC_SUCCESS, gas_left}; + } -struct EvmPrintfHandler: public PrintfHandler { - EvmPrintfHandler(ExecutionState& _state, StackTop& _stack): state(_state), stack(_stack) {} - uint64_t GetArgNumber() override { - auto val = (uint64_t)stack.pop(); - return val; + static inline Result call(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return call_impl(stack, gas_left, state, OP_CALL); } - const char* GetArgString() override{ - auto val = stack.pop(); - assert(state.memory.size() > val); - auto str = reinterpret_cast(&state.memory[(uint64_t)val]); - return str; + + static inline Result callcode(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return call_impl(stack, gas_left, state, OP_CALLCODE); } - void Puts(const char* data) override { - ss += data; + + static inline Result delegatecall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return call_impl(stack, gas_left, state, OP_DELEGATECALL); } - void Putc(char ch) override { - ss += ch; + + static inline Result staticcall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return call_impl(stack, gas_left, state, OP_STATICCALL); } - std::string ss; - ExecutionState& state; - StackTop stack; -}; -inline DynStackResult printf( - StackTop stack, int64_t gas_left, [[maybe_unused]] ExecutionState& state) noexcept -{ - auto stack_origin = stack; + static Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state, Opcode Op) noexcept { + assert(Op == OP_CREATE || Op == OP_CREATE2); - auto& fmt_ptr = stack.pop(); - assert(fmt_ptr <= std::numeric_limits::max()); + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - const char* fmt_str = reinterpret_cast(&state.memory[(uint64_t)fmt_ptr]); + const auto endowment = stack.pop(); + const auto init_code_offset_u256 = stack.pop(); + const auto init_code_size_u256 = stack.pop(); + const auto salt = (Op == OP_CREATE2) ? stack.pop() : 0; - EvmPrintfHandler handler(state, stack); - auto res [[maybe_unused]] = PrintfParser::Parse(fmt_str, handler); - assert(res == 0); - std::cout << handler.ss; + stack.push(0); // Assume failure. + state.return_data.clear(); - auto stack_pop = &stack_origin.top() - &handler.stack.top(); - assert(stack_pop < std::numeric_limits::max()); + if (!check_memory(gas_left, state.memory, init_code_offset_u256, init_code_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; - return {EVMC_SUCCESS, gas_left, int16_t(stack_pop)}; -} + const auto init_code_offset = init_code_offset_u256.to_uint64(); + const auto init_code_size = init_code_size_u256.to_uint64(); -template -Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -inline constexpr auto call = call_impl; -inline constexpr auto callcode = call_impl; -inline constexpr auto delegatecall = call_impl; -inline constexpr auto staticcall = call_impl; + if (state.rev >= EVMC_SHANGHAI && init_code_size > 0xC000) + return {EVMC_OUT_OF_GAS, gas_left}; -template -Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -inline constexpr auto create = create_impl; -inline constexpr auto create2 = create_impl; + const auto init_code_word_cost = 6 * (Op == OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI); + const auto init_code_cost = num_words(init_code_size) * init_code_word_cost; + if ((gas_left -= init_code_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; -inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto index = read_uint16_be(&pos[1]); - const auto& header = state.analysis.baseline->eof_header; - const auto stack_size = &stack.top() - state.stack_space.bottom(); + if (state.msg->depth >= 1024) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. - const auto callee_required_stack_size = - header.types[index].max_stack_height - header.types[index].inputs; - if (stack_size + callee_required_stack_size > StackSpace::limit) - { - state.status = EVMC_STACK_OVERFLOW; - return nullptr; + if (endowment != 0 && + nil::blueprint::zkevm_word(state.host.get_balance(state.msg->recipient)) < endowment) + return {EVMC_SUCCESS, gas_left}; // "Light" failure. + + auto msg = evmc_message{}; + msg.gas = gas_left; + if (state.rev >= EVMC_TANGERINE_WHISTLE) + msg.gas = msg.gas - msg.gas / 64; + + msg.kind = (Op == OP_CREATE) ? EVMC_CREATE : EVMC_CREATE2; + if (init_code_size > 0) + { + // init_code_offset may be garbage if init_code_size == 0. + msg.input_data = &state.memory[init_code_offset]; + msg.input_size = init_code_size; + } + msg.sender = state.msg->recipient; + msg.depth = state.msg->depth + 1; + msg.create2_salt = salt.to_uint256be(); + msg.value = endowment.to_uint256be(); + + const auto result = state.host.call(msg); + gas_left -= msg.gas - result.gas_left; + state.gas_refund += result.gas_refund; + + state.return_data.assign(result.output_data, result.output_size); + if (result.status_code == EVMC_SUCCESS) + stack.top() = nil::blueprint::zkevm_word(result.create_address); + + return {EVMC_SUCCESS, gas_left}; } - if (state.call_stack.size() >= StackSpace::limit) - { - // TODO: Add different error code. - state.status = EVMC_STACK_OVERFLOW; - return nullptr; + static inline Result create(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return create_impl(stack, gas_left, state, OP_CREATE); } - state.call_stack.push_back(pos + 3); - const auto offset = header.code_offsets[index] - header.code_offsets[0]; - auto code = state.analysis.baseline->executable_code; - return code.data() + offset; -} + static inline constexpr auto create2(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return create_impl(stack, gas_left, state, OP_CREATE2); + } -inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept -{ - const auto p = state.call_stack.back(); - state.call_stack.pop_back(); - return p; -} + static inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + { + const auto index = read_uint16_be(&pos[1]); + const auto& header = state.analysis.baseline->eof_header; + const auto stack_size = &stack.top() - state.stack_space.bottom(); -inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept -{ - const auto index = read_uint16_be(&pos[1]); - const auto& header = state.analysis.baseline->eof_header; - const auto stack_size = &stack.top() - state.stack_space.bottom(); + const auto callee_required_stack_size = + header.types[index].max_stack_height - header.types[index].inputs; + if (stack_size + callee_required_stack_size > StackSpace::limit) + { + state.status = EVMC_STACK_OVERFLOW; + return nullptr; + } + + if (state.call_stack.size() >= StackSpace::limit) + { + // TODO: Add different error code. + state.status = EVMC_STACK_OVERFLOW; + return nullptr; + } + state.call_stack.push_back(pos + 3); + + const auto offset = header.code_offsets[index] - header.code_offsets[0]; + auto code = state.analysis.baseline->executable_code; + return code.data() + offset; + } - const auto callee_required_stack_size = - header.types[index].max_stack_height - header.types[index].inputs; - if (stack_size + callee_required_stack_size > StackSpace::limit) + static inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept { - state.status = EVMC_STACK_OVERFLOW; - return nullptr; + const auto p = state.call_stack.back(); + state.call_stack.pop_back(); + return p; } - const auto offset = header.code_offsets[index] - header.code_offsets[0]; - const auto code = state.analysis.baseline->executable_code; - return code.data() + offset; -} + static inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + { + const auto index = read_uint16_be(&pos[1]); + const auto& header = state.analysis.baseline->eof_header; + const auto stack_size = &stack.top() - state.stack_space.bottom(); -template -inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - const auto& offset = stack[0]; - const auto& size = stack[1]; + const auto callee_required_stack_size = + header.types[index].max_stack_height - header.types[index].inputs; + if (stack_size + callee_required_stack_size > StackSpace::limit) + { + state.status = EVMC_STACK_OVERFLOW; + return nullptr; + } - if (!check_memory(gas_left, state.memory, offset, size)) - return {EVMC_OUT_OF_GAS, gas_left}; + const auto offset = header.code_offsets[index] - header.code_offsets[0]; + const auto code = state.analysis.baseline->executable_code; + return code.data() + offset; + } - state.output_size = static_cast(size); - if (state.output_size != 0) - state.output_offset = static_cast(offset); - return {StatusCode, gas_left}; -} -inline constexpr auto return_ = return_impl; -inline constexpr auto revert = return_impl; + static inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state, evmc_status_code StatusCode) noexcept + { + const auto& offset = stack[0]; + const auto& size = stack[1]; -inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + if (!check_memory(gas_left, state.memory, offset, size)) + return {EVMC_OUT_OF_GAS, gas_left}; - const auto beneficiary = intx::be::trunc(stack[0]); + state.output_size = size.to_uint64(); + if (state.output_size != 0) + state.output_offset = offset.to_uint64(); + return {StatusCode, gas_left}; + } + static inline TermResult return_(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return return_impl(stack, gas_left, state, EVMC_SUCCESS); + } - if (state.rev >= EVMC_BERLIN && state.host.access_account(beneficiary) == EVMC_ACCESS_COLD) - { - if ((gas_left -= instr::cold_account_access_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + static inline TermResult revert(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + return return_impl(stack, gas_left, state, EVMC_REVERT); } - if (state.rev >= EVMC_TANGERINE_WHISTLE) + static inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - if (state.rev == EVMC_TANGERINE_WHISTLE || state.host.get_balance(state.msg->recipient)) + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + + const auto beneficiary = stack[0].to_address(); + + if (state.rev >= EVMC_BERLIN && state.host.access_account(beneficiary) == EVMC_ACCESS_COLD) + { + if ((gas_left -= instr::cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } + + if (state.rev >= EVMC_TANGERINE_WHISTLE) { - // After TANGERINE_WHISTLE apply additional cost of - // sending value to a non-existing account. - if (!state.host.account_exists(beneficiary)) + if (state.rev == EVMC_TANGERINE_WHISTLE || state.host.get_balance(state.msg->recipient)) { - if ((gas_left -= 25000) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + // After TANGERINE_WHISTLE apply additional cost of + // sending value to a non-existing account. + if (!state.host.account_exists(beneficiary)) + { + if ((gas_left -= 25000) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } } } + + if (state.host.selfdestruct(state.msg->recipient, beneficiary)) + { + if (state.rev < EVMC_LONDON) + state.gas_refund += 24000; + } + return {EVMC_SUCCESS, gas_left}; } - if (state.host.selfdestruct(state.msg->recipient, beneficiary)) + static Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { - if (state.rev < EVMC_LONDON) - state.gas_refund += 24000; + auto& x = stack.top(); + const auto key = x.to_uint256be(); + + if (state.rev >= EVMC_BERLIN && + state.host.access_storage(state.msg->recipient, key) == EVMC_ACCESS_COLD) + { + // The warm storage access cost is already applied (from the cost table). + // Here we need to apply additional cold storage access cost. + constexpr auto additional_cold_sload_cost = + instr::cold_sload_cost - instr::warm_storage_read_cost; + if ((gas_left -= additional_cold_sload_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + } + + x = nil::blueprint::zkevm_word(state.host.get_storage(state.msg->recipient, key)); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 2) = x.to_uint64(); + + return {EVMC_SUCCESS, gas_left}; } - return {EVMC_SUCCESS, gas_left}; -} + static Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + { + if (state.in_static_mode()) + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; -/// Maps an opcode to the instruction implementation. -/// -/// The set of template specializations which map opcodes `Op` to the function -/// implementing the instruction identified by the opcode. -/// instr::impl(/*...*/); -/// The unspecialized template is invalid and should never to used. -template -inline constexpr auto impl = nullptr; - -#undef ON_OPCODE_IDENTIFIER -#define ON_OPCODE_IDENTIFIER(OPCODE, IDENTIFIER) \ - template <> \ - inline constexpr auto impl = IDENTIFIER; // opcode -> implementation -MAP_OPCODES -#undef ON_OPCODE_IDENTIFIER -#define ON_OPCODE_IDENTIFIER ON_OPCODE_IDENTIFIER_DEFAULT + if (state.rev >= EVMC_ISTANBUL && gas_left <= 2300) + return {EVMC_OUT_OF_GAS, gas_left}; + + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 0) = stack[1].to_uint64(); + state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 1) = stack[0].to_uint64(); + const auto key = stack.pop().to_uint256be(); + const auto value = stack.pop().to_uint256be(); + + const auto gas_cost_cold = + (state.rev >= EVMC_BERLIN && + state.host.access_storage(state.msg->recipient, key) == EVMC_ACCESS_COLD) ? + instr::cold_sload_cost : + 0; + const auto status = state.host.set_storage(state.msg->recipient, key, value); + + const auto [gas_cost_warm, gas_refund] = sstore_costs[state.rev][status]; + const auto gas_cost = gas_cost_warm + gas_cost_cold; + if ((gas_left -= gas_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + state.gas_refund += gas_refund; + return {EVMC_SUCCESS, gas_left}; + } +}; // class operation } // namespace instr::core } // namespace evmone diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp deleted file mode 100644 index 0e677d36..00000000 --- a/lib/evmone/instructions_calls.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2019 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#include "eof.hpp" -#include "instructions.hpp" - -namespace evmone::instr::core -{ -template -Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - static_assert( - Op == OP_CALL || Op == OP_CALLCODE || Op == OP_DELEGATECALL || Op == OP_STATICCALL); - - const auto gas = stack.pop(); - const auto dst = intx::be::trunc(stack.pop()); - const auto value = (Op == OP_STATICCALL || Op == OP_DELEGATECALL) ? 0 : stack.pop(); - const auto has_value = value != 0; - const auto input_offset_u256 = stack.pop(); - const auto input_size_u256 = stack.pop(); - const auto output_offset_u256 = stack.pop(); - const auto output_size_u256 = stack.pop(); - - stack.push(0); // Assume failure. - state.return_data.clear(); - - if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD) - { - if ((gas_left -= instr::additional_cold_account_access_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - } - - if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) - return {EVMC_OUT_OF_GAS, gas_left}; - - if (!check_memory(gas_left, state.memory, output_offset_u256, output_size_u256)) - return {EVMC_OUT_OF_GAS, gas_left}; - - const auto input_offset = static_cast(input_offset_u256); - const auto input_size = static_cast(input_size_u256); - const auto output_offset = static_cast(output_offset_u256); - const auto output_size = static_cast(output_size_u256); - - auto msg = evmc_message{}; - msg.kind = (Op == OP_DELEGATECALL) ? EVMC_DELEGATECALL : - (Op == OP_CALLCODE) ? EVMC_CALLCODE : - EVMC_CALL; - msg.flags = (Op == OP_STATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; - msg.depth = state.msg->depth + 1; - msg.recipient = (Op == OP_CALL || Op == OP_STATICCALL) ? dst : state.msg->recipient; - msg.code_address = dst; - msg.sender = (Op == OP_DELEGATECALL) ? state.msg->sender : state.msg->recipient; - msg.value = - (Op == OP_DELEGATECALL) ? state.msg->value : intx::be::store(value); - - if (input_size > 0) - { - // input_offset may be garbage if input_size == 0. - msg.input_data = &state.memory[input_offset]; - msg.input_size = input_size; - } - - auto cost = has_value ? 9000 : 0; - - if constexpr (Op == OP_CALL) - { - if (has_value && state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - - if ((has_value || state.rev < EVMC_SPURIOUS_DRAGON) && !state.host.account_exists(dst)) - cost += 25000; - } - - if ((gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - - msg.gas = std::numeric_limits::max(); - if (gas < msg.gas) - msg.gas = static_cast(gas); - - if (state.rev >= EVMC_TANGERINE_WHISTLE) // TODO: Always true for STATICCALL. - msg.gas = std::min(msg.gas, gas_left - gas_left / 64); - else if (msg.gas > gas_left) - return {EVMC_OUT_OF_GAS, gas_left}; - - if (has_value) - { - msg.gas += 2300; // Add stipend. - gas_left += 2300; - } - - if (state.msg->depth >= 1024) - return {EVMC_SUCCESS, gas_left}; // "Light" failure. - - if (has_value && intx::be::load(state.host.get_balance(state.msg->recipient)) < value) - return {EVMC_SUCCESS, gas_left}; // "Light" failure. - - if constexpr (Op == OP_DELEGATECALL) - { - if (state.rev >= EVMC_PRAGUE && is_eof_container(state.original_code)) - { - // The code targeted by DELEGATECALL must also be an EOF. - // This restriction has been added to EIP-3540 in - // https://github.com/ethereum/EIPs/pull/7131 - uint8_t target_code_prefix[2]; - const auto s = state.host.copy_code( - msg.code_address, 0, target_code_prefix, std::size(target_code_prefix)); - if (!is_eof_container({target_code_prefix, s})) - return {EVMC_SUCCESS, gas_left}; - } - } - - const auto result = state.host.call(msg); - state.return_data.assign(result.output_data, result.output_size); - stack.top() = result.status_code == EVMC_SUCCESS; - - if (const auto copy_size = std::min(output_size, result.output_size); copy_size > 0) - std::memcpy(&state.memory[output_offset], result.output_data, copy_size); - - const auto gas_used = msg.gas - result.gas_left; - gas_left -= gas_used; - state.gas_refund += result.gas_refund; - return {EVMC_SUCCESS, gas_left}; -} - -template Result call_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -template Result call_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -template Result call_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -template Result call_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; - - -template -Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - static_assert(Op == OP_CREATE || Op == OP_CREATE2); - - if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - - const auto endowment = stack.pop(); - const auto init_code_offset_u256 = stack.pop(); - const auto init_code_size_u256 = stack.pop(); - const auto salt = (Op == OP_CREATE2) ? stack.pop() : uint256{}; - - stack.push(0); // Assume failure. - state.return_data.clear(); - - if (!check_memory(gas_left, state.memory, init_code_offset_u256, init_code_size_u256)) - return {EVMC_OUT_OF_GAS, gas_left}; - - const auto init_code_offset = static_cast(init_code_offset_u256); - const auto init_code_size = static_cast(init_code_size_u256); - - if (state.rev >= EVMC_SHANGHAI && init_code_size > 0xC000) - return {EVMC_OUT_OF_GAS, gas_left}; - - const auto init_code_word_cost = 6 * (Op == OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI); - const auto init_code_cost = num_words(init_code_size) * init_code_word_cost; - if ((gas_left -= init_code_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - - if (state.msg->depth >= 1024) - return {EVMC_SUCCESS, gas_left}; // "Light" failure. - - if (endowment != 0 && - intx::be::load(state.host.get_balance(state.msg->recipient)) < endowment) - return {EVMC_SUCCESS, gas_left}; // "Light" failure. - - auto msg = evmc_message{}; - msg.gas = gas_left; - if (state.rev >= EVMC_TANGERINE_WHISTLE) - msg.gas = msg.gas - msg.gas / 64; - - msg.kind = (Op == OP_CREATE) ? EVMC_CREATE : EVMC_CREATE2; - if (init_code_size > 0) - { - // init_code_offset may be garbage if init_code_size == 0. - msg.input_data = &state.memory[init_code_offset]; - msg.input_size = init_code_size; - } - msg.sender = state.msg->recipient; - msg.depth = state.msg->depth + 1; - msg.create2_salt = intx::be::store(salt); - msg.value = intx::be::store(endowment); - - const auto result = state.host.call(msg); - gas_left -= msg.gas - result.gas_left; - state.gas_refund += result.gas_refund; - - state.return_data.assign(result.output_data, result.output_size); - if (result.status_code == EVMC_SUCCESS) - stack.top() = intx::be::load(result.create_address); - - return {EVMC_SUCCESS, gas_left}; -} - -template Result create_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -template Result create_impl( - StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -} // namespace evmone::instr::core diff --git a/lib/evmone/instructions_storage.cpp b/lib/evmone/instructions_storage.cpp deleted file mode 100644 index e0d1afb7..00000000 --- a/lib/evmone/instructions_storage.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2019 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#include "instructions.hpp" - -namespace evmone::instr::core -{ -namespace -{ -/// The gas cost specification for storage instructions. -struct StorageCostSpec -{ - bool net_cost; ///< Is this net gas cost metering schedule? - int16_t warm_access; ///< Storage warm access cost, YP: G_{warmaccess} - int16_t set; ///< Storage addition cost, YP: G_{sset} - int16_t reset; ///< Storage modification cost, YP: G_{sreset} - int16_t clear; ///< Storage deletion refund, YP: R_{sclear} -}; - -/// Table of gas cost specification for storage instructions per EVM revision. -/// TODO: This can be moved to instruction traits and be used in other places: e.g. -/// SLOAD cost, replacement for warm_storage_read_cost. -constexpr auto storage_cost_spec = []() noexcept { - std::array tbl{}; - - // Legacy cost schedule. - for (auto rev : {EVMC_FRONTIER, EVMC_HOMESTEAD, EVMC_TANGERINE_WHISTLE, EVMC_SPURIOUS_DRAGON, - EVMC_BYZANTIUM, EVMC_PETERSBURG}) - tbl[rev] = {false, 200, 20000, 5000, 15000}; - - // Net cost schedule. - tbl[EVMC_CONSTANTINOPLE] = {true, 200, 20000, 5000, 15000}; - tbl[EVMC_ISTANBUL] = {true, 800, 20000, 5000, 15000}; - tbl[EVMC_BERLIN] = { - true, instr::warm_storage_read_cost, 20000, 5000 - instr::cold_sload_cost, 15000}; - tbl[EVMC_LONDON] = { - true, instr::warm_storage_read_cost, 20000, 5000 - instr::cold_sload_cost, 4800}; - tbl[EVMC_PARIS] = tbl[EVMC_LONDON]; - tbl[EVMC_SHANGHAI] = tbl[EVMC_LONDON]; - tbl[EVMC_CANCUN] = tbl[EVMC_LONDON]; - tbl[EVMC_PRAGUE] = tbl[EVMC_LONDON]; - return tbl; -}(); - - -struct StorageStoreCost -{ - int16_t gas_cost; - int16_t gas_refund; -}; - -// The lookup table of SSTORE costs by the storage update status. -constexpr auto sstore_costs = []() noexcept { - std::array, - EVMC_MAX_REVISION + 1> - tbl{}; - - for (size_t rev = EVMC_FRONTIER; rev <= EVMC_MAX_REVISION; ++rev) - { - auto& e = tbl[rev]; - if (const auto c = storage_cost_spec[rev]; !c.net_cost) // legacy - { - e[EVMC_STORAGE_ADDED] = {c.set, 0}; - e[EVMC_STORAGE_DELETED] = {c.reset, c.clear}; - e[EVMC_STORAGE_MODIFIED] = {c.reset, 0}; - e[EVMC_STORAGE_ASSIGNED] = e[EVMC_STORAGE_MODIFIED]; - e[EVMC_STORAGE_DELETED_ADDED] = e[EVMC_STORAGE_ADDED]; - e[EVMC_STORAGE_MODIFIED_DELETED] = e[EVMC_STORAGE_DELETED]; - e[EVMC_STORAGE_DELETED_RESTORED] = e[EVMC_STORAGE_ADDED]; - e[EVMC_STORAGE_ADDED_DELETED] = e[EVMC_STORAGE_DELETED]; - e[EVMC_STORAGE_MODIFIED_RESTORED] = e[EVMC_STORAGE_MODIFIED]; - } - else // net cost - { - e[EVMC_STORAGE_ASSIGNED] = {c.warm_access, 0}; - e[EVMC_STORAGE_ADDED] = {c.set, 0}; - e[EVMC_STORAGE_DELETED] = {c.reset, c.clear}; - e[EVMC_STORAGE_MODIFIED] = {c.reset, 0}; - e[EVMC_STORAGE_DELETED_ADDED] = {c.warm_access, static_cast(-c.clear)}; - e[EVMC_STORAGE_MODIFIED_DELETED] = {c.warm_access, c.clear}; - e[EVMC_STORAGE_DELETED_RESTORED] = { - c.warm_access, static_cast(c.reset - c.warm_access - c.clear)}; - e[EVMC_STORAGE_ADDED_DELETED] = { - c.warm_access, static_cast(c.set - c.warm_access)}; - e[EVMC_STORAGE_MODIFIED_RESTORED] = { - c.warm_access, static_cast(c.reset - c.warm_access)}; - } - } - - return tbl; -}(); -} // namespace - -Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - auto& x = stack.top(); - const auto key = intx::be::store(x); - - if (state.rev >= EVMC_BERLIN && - state.host.access_storage(state.msg->recipient, key) == EVMC_ACCESS_COLD) - { - // The warm storage access cost is already applied (from the cost table). - // Here we need to apply additional cold storage access cost. - constexpr auto additional_cold_sload_cost = - instr::cold_sload_cost - instr::warm_storage_read_cost; - if ((gas_left -= additional_cold_sload_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - } - - x = intx::be::load(state.host.get_storage(state.msg->recipient, key)); - state.m_handler->set_witness(static_cast(state.msg->depth), 3, 2, x); - - return {EVMC_SUCCESS, gas_left}; -} - -Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept -{ - if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - - if (state.rev >= EVMC_ISTANBUL && gas_left <= 2300) - return {EVMC_OUT_OF_GAS, gas_left}; - - state.m_handler->set_witness(static_cast(state.msg->depth), 3, 0, stack[1]); - state.m_handler->set_witness(static_cast(state.msg->depth), 3, 1, stack[0]); - const auto key = intx::be::store(stack.pop()); - const auto value = intx::be::store(stack.pop()); - - const auto gas_cost_cold = - (state.rev >= EVMC_BERLIN && - state.host.access_storage(state.msg->recipient, key) == EVMC_ACCESS_COLD) ? - instr::cold_sload_cost : - 0; - const auto status = state.host.set_storage(state.msg->recipient, key, value); - - const auto [gas_cost_warm, gas_refund] = sstore_costs[state.rev][status]; - const auto gas_cost = gas_cost_warm + gas_cost_cold; - if ((gas_left -= gas_cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; - state.gas_refund += gas_refund; - return {EVMC_SUCCESS, gas_left}; -} -} // namespace evmone::instr::core diff --git a/lib/evmone/instructions_xmacro.hpp b/lib/evmone/instructions_xmacro.hpp index 505c2937..b41fbcd8 100644 --- a/lib/evmone/instructions_xmacro.hpp +++ b/lib/evmone/instructions_xmacro.hpp @@ -32,41 +32,41 @@ /// /// See for more about X Macros: https://en.wikipedia.org/wiki/X_Macro. #define MAP_OPCODES \ - ON_OPCODE_IDENTIFIER(OP_STOP, stop) \ - ON_OPCODE_IDENTIFIER(OP_ADD, add) \ - ON_OPCODE_IDENTIFIER(OP_MUL, mul) \ - ON_OPCODE_IDENTIFIER(OP_SUB, sub) \ - ON_OPCODE_IDENTIFIER(OP_DIV, div) \ - ON_OPCODE_IDENTIFIER(OP_SDIV, sdiv) \ - ON_OPCODE_IDENTIFIER(OP_MOD, mod) \ - ON_OPCODE_IDENTIFIER(OP_SMOD, smod) \ - ON_OPCODE_IDENTIFIER(OP_ADDMOD, addmod) \ - ON_OPCODE_IDENTIFIER(OP_MULMOD, mulmod) \ - ON_OPCODE_IDENTIFIER(OP_EXP, exp) \ - ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, signextend) \ + ON_OPCODE_IDENTIFIER(OP_STOP, operation::stop) \ + ON_OPCODE_IDENTIFIER(OP_ADD, operation::add) \ + ON_OPCODE_IDENTIFIER(OP_MUL, operation::mul) \ + ON_OPCODE_IDENTIFIER(OP_SUB, operation::sub) \ + ON_OPCODE_IDENTIFIER(OP_DIV, operation::div) \ + ON_OPCODE_IDENTIFIER(OP_SDIV, operation::sdiv) \ + ON_OPCODE_IDENTIFIER(OP_MOD, operation::mod) \ + ON_OPCODE_IDENTIFIER(OP_SMOD, operation::smod) \ + ON_OPCODE_IDENTIFIER(OP_ADDMOD, operation::addmod) \ + ON_OPCODE_IDENTIFIER(OP_MULMOD, operation::mulmod) \ + ON_OPCODE_IDENTIFIER(OP_EXP, operation::exp) \ + ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, operation::signextend) \ ON_OPCODE_UNDEFINED(0x0c) \ ON_OPCODE_UNDEFINED(0x0d) \ ON_OPCODE_UNDEFINED(0x0e) \ ON_OPCODE_UNDEFINED(0x0f) \ \ - ON_OPCODE_IDENTIFIER(OP_LT, lt) \ - ON_OPCODE_IDENTIFIER(OP_GT, gt) \ - ON_OPCODE_IDENTIFIER(OP_SLT, slt) \ - ON_OPCODE_IDENTIFIER(OP_SGT, sgt) \ - ON_OPCODE_IDENTIFIER(OP_EQ, eq) \ - ON_OPCODE_IDENTIFIER(OP_ISZERO, iszero) \ - ON_OPCODE_IDENTIFIER(OP_AND, and_) \ - ON_OPCODE_IDENTIFIER(OP_OR, or_) \ - ON_OPCODE_IDENTIFIER(OP_XOR, xor_) \ - ON_OPCODE_IDENTIFIER(OP_NOT, not_) \ - ON_OPCODE_IDENTIFIER(OP_BYTE, byte) \ - ON_OPCODE_IDENTIFIER(OP_SHL, shl) \ - ON_OPCODE_IDENTIFIER(OP_SHR, shr) \ - ON_OPCODE_IDENTIFIER(OP_SAR, sar) \ + ON_OPCODE_IDENTIFIER(OP_LT, operation::lt) \ + ON_OPCODE_IDENTIFIER(OP_GT, operation::gt) \ + ON_OPCODE_IDENTIFIER(OP_SLT, operation::slt) \ + ON_OPCODE_IDENTIFIER(OP_SGT, operation::sgt) \ + ON_OPCODE_IDENTIFIER(OP_EQ, operation::eq) \ + ON_OPCODE_IDENTIFIER(OP_ISZERO, operation::iszero) \ + ON_OPCODE_IDENTIFIER(OP_AND, operation::and_) \ + ON_OPCODE_IDENTIFIER(OP_OR, operation::or_) \ + ON_OPCODE_IDENTIFIER(OP_XOR, operation::xor_) \ + ON_OPCODE_IDENTIFIER(OP_NOT, operation::not_) \ + ON_OPCODE_IDENTIFIER(OP_BYTE, operation::byte) \ + ON_OPCODE_IDENTIFIER(OP_SHL, operation::shl) \ + ON_OPCODE_IDENTIFIER(OP_SHR, operation::shr) \ + ON_OPCODE_IDENTIFIER(OP_SAR, operation::sar) \ ON_OPCODE_UNDEFINED(0x1e) \ ON_OPCODE_UNDEFINED(0x1f) \ \ - ON_OPCODE_IDENTIFIER(OP_KECCAK256, keccak256) \ + ON_OPCODE_IDENTIFIER(OP_KECCAK256, operation::keccak256) \ ON_OPCODE_UNDEFINED(0x21) \ ON_OPCODE_UNDEFINED(0x22) \ ON_OPCODE_UNDEFINED(0x23) \ @@ -83,140 +83,134 @@ ON_OPCODE_UNDEFINED(0x2e) \ ON_OPCODE_UNDEFINED(0x2f) \ \ - ON_OPCODE_IDENTIFIER(OP_ADDRESS, address) \ - ON_OPCODE_IDENTIFIER(OP_BALANCE, balance) \ - ON_OPCODE_IDENTIFIER(OP_ORIGIN, origin) \ - ON_OPCODE_IDENTIFIER(OP_CALLER, caller) \ - ON_OPCODE_IDENTIFIER(OP_CALLVALUE, callvalue) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, calldataload) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, calldatasize) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, calldatacopy) \ - ON_OPCODE_IDENTIFIER(OP_CODESIZE, codesize) \ - ON_OPCODE_IDENTIFIER(OP_CODECOPY, codecopy) \ - ON_OPCODE_IDENTIFIER(OP_GASPRICE, gasprice) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, extcodesize) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, extcodecopy) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, returndatasize) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, returndatacopy) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, extcodehash) \ + ON_OPCODE_IDENTIFIER(OP_ADDRESS, operation::address) \ + ON_OPCODE_IDENTIFIER(OP_BALANCE, operation::balance) \ + ON_OPCODE_IDENTIFIER(OP_ORIGIN, operation::origin) \ + ON_OPCODE_IDENTIFIER(OP_CALLER, operation::caller) \ + ON_OPCODE_IDENTIFIER(OP_CALLVALUE, operation::callvalue) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, operation::calldataload) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, operation::calldatasize) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, operation::calldatacopy) \ + ON_OPCODE_IDENTIFIER(OP_CODESIZE, operation::codesize) \ + ON_OPCODE_IDENTIFIER(OP_CODECOPY, operation::codecopy) \ + ON_OPCODE_IDENTIFIER(OP_GASPRICE, operation::gasprice) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, operation::extcodesize) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, operation::extcodecopy) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, operation::returndatasize) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, operation::returndatacopy) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, operation::extcodehash) \ \ - ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, blockhash) \ - ON_OPCODE_IDENTIFIER(OP_COINBASE, coinbase) \ - ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, timestamp) \ - ON_OPCODE_IDENTIFIER(OP_NUMBER, number) \ - ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, prevrandao) \ - ON_OPCODE_IDENTIFIER(OP_GASLIMIT, gaslimit) \ - ON_OPCODE_IDENTIFIER(OP_CHAINID, chainid) \ - ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, selfbalance) \ - ON_OPCODE_IDENTIFIER(OP_BASEFEE, basefee) \ - ON_OPCODE_IDENTIFIER(OP_BLOBHASH, blobhash) \ - ON_OPCODE_IDENTIFIER(OP_BLOBBASEFEE, blobbasefee) \ + ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, operation::blockhash) \ + ON_OPCODE_IDENTIFIER(OP_COINBASE, operation::coinbase) \ + ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, operation::timestamp) \ + ON_OPCODE_IDENTIFIER(OP_NUMBER, operation::number) \ + ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, operation::prevrandao) \ + ON_OPCODE_IDENTIFIER(OP_GASLIMIT, operation::gaslimit) \ + ON_OPCODE_IDENTIFIER(OP_CHAINID, operation::chainid) \ + ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, operation::selfbalance) \ + ON_OPCODE_IDENTIFIER(OP_BASEFEE, operation::basefee) \ + ON_OPCODE_IDENTIFIER(OP_BLOBHASH, operation::blobhash) \ + ON_OPCODE_IDENTIFIER(OP_BLOBBASEFEE, operation::blobbasefee) \ ON_OPCODE_UNDEFINED(0x4b) \ ON_OPCODE_UNDEFINED(0x4c) \ ON_OPCODE_UNDEFINED(0x4d) \ ON_OPCODE_UNDEFINED(0x4e) \ ON_OPCODE_UNDEFINED(0x4f) \ \ - ON_OPCODE_IDENTIFIER(OP_POP, pop) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD, mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD8, mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD16, mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD32, mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD64, mload) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE, mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE8, mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE16, mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE32, mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE64, mstore) \ - ON_OPCODE_IDENTIFIER(OP_SLOAD, sload) \ - ON_OPCODE_IDENTIFIER(OP_SSTORE, sstore) \ - ON_OPCODE_IDENTIFIER(OP_JUMP, jump) \ - ON_OPCODE_IDENTIFIER(OP_JUMPI, jumpi) \ - ON_OPCODE_IDENTIFIER(OP_PC, pc) \ - ON_OPCODE_IDENTIFIER(OP_MSIZE, msize) \ - ON_OPCODE_IDENTIFIER(OP_GAS, gas) \ - ON_OPCODE_IDENTIFIER(OP_JUMPDEST, jumpdest) \ - ON_OPCODE_IDENTIFIER(OP_TLOAD, tload) \ - ON_OPCODE_IDENTIFIER(OP_TSTORE, tstore) \ - ON_OPCODE_IDENTIFIER(OP_MCOPY, mcopy) \ - ON_OPCODE_IDENTIFIER(OP_PUSH0, push0) \ + ON_OPCODE_IDENTIFIER(OP_POP, operation::pop) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD, operation::template mload::value_type>) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD8, operation::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD16, operation::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD32, operation::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD64, operation::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE, operation::template mstore::value_type>) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE8, operation::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE16, operation::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE32, operation::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE64, operation::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_SLOAD, operation::sload) \ + ON_OPCODE_IDENTIFIER(OP_SSTORE, operation::sstore) \ + ON_OPCODE_IDENTIFIER(OP_JUMP, operation::jump) \ + ON_OPCODE_IDENTIFIER(OP_JUMPI, operation::jumpi) \ + ON_OPCODE_IDENTIFIER(OP_PC, operation::pc) \ + ON_OPCODE_IDENTIFIER(OP_MSIZE, operation::msize) \ + ON_OPCODE_IDENTIFIER(OP_GAS, operation::gas) \ + ON_OPCODE_IDENTIFIER(OP_JUMPDEST, operation::jumpdest) \ + ON_OPCODE_IDENTIFIER(OP_TLOAD, operation::tload) \ + ON_OPCODE_IDENTIFIER(OP_TSTORE, operation::tstore) \ + ON_OPCODE_IDENTIFIER(OP_MCOPY, operation::mcopy) \ + ON_OPCODE_IDENTIFIER(OP_PUSH0, operation::push0) \ \ - ON_OPCODE_IDENTIFIER(OP_PUSH1, push<1>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH2, push<2>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH3, push<3>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH4, push<4>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH5, push<5>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH6, push<6>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH7, push<7>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH8, push<8>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH9, push<9>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH10, push<10>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH11, push<11>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH12, push<12>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH13, push<13>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH14, push<14>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH15, push<15>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH1, operation::template push<1>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH2, operation::template push<2>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH3, operation::template push<3>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH4, operation::template push<4>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH5, operation::template push<5>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH6, operation::template push<6>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH7, operation::template push<7>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH8, operation::template push<8>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH9, operation::template push<9>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH10, operation::template push<10>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH11, operation::template push<11>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH12, operation::template push<12>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH13, operation::template push<13>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH14, operation::template push<14>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH15, operation::template push<15>) \ \ - ON_OPCODE_IDENTIFIER(OP_PUSH16, push<16>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH17, push<17>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH18, push<18>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH19, push<19>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH20, push<20>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH21, push<21>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH22, push<22>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH23, push<23>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH24, push<24>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH25, push<25>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH26, push<26>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH27, push<27>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH28, push<28>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH29, push<29>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH30, push<30>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH31, push<31>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH32, push<32>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH16, operation::template push<16>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH17, operation::template push<17>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH18, operation::template push<18>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH19, operation::template push<19>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH20, operation::template push<20>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH21, operation::template push<21>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH22, operation::template push<22>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH23, operation::template push<23>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH24, operation::template push<24>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH25, operation::template push<25>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH26, operation::template push<26>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH27, operation::template push<27>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH28, operation::template push<28>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH29, operation::template push<29>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH30, operation::template push<30>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH31, operation::template push<31>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH32, operation::template push<32>) \ \ - ON_OPCODE_IDENTIFIER(OP_DUP1, dup<1>) \ - ON_OPCODE_IDENTIFIER(OP_DUP2, dup<2>) \ - ON_OPCODE_IDENTIFIER(OP_DUP3, dup<3>) \ - ON_OPCODE_IDENTIFIER(OP_DUP4, dup<4>) \ - ON_OPCODE_IDENTIFIER(OP_DUP5, dup<5>) \ - ON_OPCODE_IDENTIFIER(OP_DUP6, dup<6>) \ - ON_OPCODE_IDENTIFIER(OP_DUP7, dup<7>) \ - ON_OPCODE_IDENTIFIER(OP_DUP8, dup<8>) \ - ON_OPCODE_IDENTIFIER(OP_DUP9, dup<9>) \ - ON_OPCODE_IDENTIFIER(OP_DUP10, dup<10>) \ - ON_OPCODE_IDENTIFIER(OP_DUP11, dup<11>) \ - ON_OPCODE_IDENTIFIER(OP_DUP12, dup<12>) \ - ON_OPCODE_IDENTIFIER(OP_DUP13, dup<13>) \ - ON_OPCODE_IDENTIFIER(OP_DUP14, dup<14>) \ - ON_OPCODE_IDENTIFIER(OP_DUP15, dup<15>) \ - ON_OPCODE_IDENTIFIER(OP_DUP16, dup<16>) \ + ON_OPCODE_IDENTIFIER(OP_DUP1, operation::template dup<1>) \ + ON_OPCODE_IDENTIFIER(OP_DUP2, operation::template dup<2>) \ + ON_OPCODE_IDENTIFIER(OP_DUP3, operation::template dup<3>) \ + ON_OPCODE_IDENTIFIER(OP_DUP4, operation::template dup<4>) \ + ON_OPCODE_IDENTIFIER(OP_DUP5, operation::template dup<5>) \ + ON_OPCODE_IDENTIFIER(OP_DUP6, operation::template dup<6>) \ + ON_OPCODE_IDENTIFIER(OP_DUP7, operation::template dup<7>) \ + ON_OPCODE_IDENTIFIER(OP_DUP8, operation::template dup<8>) \ + ON_OPCODE_IDENTIFIER(OP_DUP9, operation::template dup<9>) \ + ON_OPCODE_IDENTIFIER(OP_DUP10, operation::template dup<10>) \ + ON_OPCODE_IDENTIFIER(OP_DUP11, operation::template dup<11>) \ + ON_OPCODE_IDENTIFIER(OP_DUP12, operation::template dup<12>) \ + ON_OPCODE_IDENTIFIER(OP_DUP13, operation::template dup<13>) \ + ON_OPCODE_IDENTIFIER(OP_DUP14, operation::template dup<14>) \ + ON_OPCODE_IDENTIFIER(OP_DUP15, operation::template dup<15>) \ + ON_OPCODE_IDENTIFIER(OP_DUP16, operation::template dup<16>) \ \ - ON_OPCODE_IDENTIFIER(OP_SWAP1, swap<1>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP2, swap<2>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP3, swap<3>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP4, swap<4>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP5, swap<5>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP6, swap<6>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP7, swap<7>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP8, swap<8>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP9, swap<9>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP10, swap<10>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP11, swap<11>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP12, swap<12>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP13, swap<13>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP14, swap<14>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP15, swap<15>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP16, swap<16>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP1, operation::template swap<1>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP2, operation::template swap<2>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP3, operation::template swap<3>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP4, operation::template swap<4>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP5, operation::template swap<5>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP6, operation::template swap<6>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP7, operation::template swap<7>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP8, operation::template swap<8>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP9, operation::template swap<9>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP10, operation::template swap<10>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP11, operation::template swap<11>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP12, operation::template swap<12>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP13, operation::template swap<13>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP14, operation::template swap<14>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP15, operation::template swap<15>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP16, operation::template swap<16>) \ \ - ON_OPCODE_IDENTIFIER(OP_LOG0, log<0>) \ - ON_OPCODE_IDENTIFIER(OP_LOG1, log<1>) \ - ON_OPCODE_IDENTIFIER(OP_LOG2, log<2>) \ - ON_OPCODE_IDENTIFIER(OP_LOG3, log<3>) \ - ON_OPCODE_IDENTIFIER(OP_LOG4, log<4>) \ - ON_OPCODE_IDENTIFIER(OP_PRINTF, printf) \ - ON_OPCODE_IDENTIFIER(OP_SWAP, swap<0>) \ - ON_OPCODE_IDENTIFIER(OP_DUP, dup<0>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP, operation::template swap<0>) \ + ON_OPCODE_IDENTIFIER(OP_DUP, operation::template dup<0>) \ ON_OPCODE_UNDEFINED(0xa8) \ ON_OPCODE_UNDEFINED(0xa9) \ ON_OPCODE_UNDEFINED(0xaa) \ @@ -270,36 +264,36 @@ ON_OPCODE_UNDEFINED(0xde) \ ON_OPCODE_UNDEFINED(0xdf) \ \ - ON_OPCODE_IDENTIFIER(OP_RJUMP, rjump) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPI, rjumpi) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPV, rjumpv) \ - ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \ - ON_OPCODE_IDENTIFIER(OP_RETF, retf) \ - ON_OPCODE_IDENTIFIER(OP_JUMPF, jumpf) \ - ON_OPCODE_IDENTIFIER(OP_DUPN, dupn) \ - ON_OPCODE_IDENTIFIER(OP_SWAPN, swapn) \ - ON_OPCODE_IDENTIFIER(OP_DATALOAD, dataload) \ - ON_OPCODE_IDENTIFIER(OP_DATALOADN, dataloadn) \ - ON_OPCODE_IDENTIFIER(OP_DATASIZE, datasize) \ - ON_OPCODE_IDENTIFIER(OP_DATACOPY, datacopy) \ + ON_OPCODE_IDENTIFIER(OP_RJUMP, operation::rjump) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPI, operation::rjumpi) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPV, operation::rjumpv) \ + ON_OPCODE_IDENTIFIER(OP_CALLF, operation::callf) \ + ON_OPCODE_IDENTIFIER(OP_RETF, operation::retf) \ + ON_OPCODE_IDENTIFIER(OP_JUMPF, operation::jumpf) \ + ON_OPCODE_IDENTIFIER(OP_DUPN, operation::dupn) \ + ON_OPCODE_IDENTIFIER(OP_SWAPN, operation::swapn) \ + ON_OPCODE_IDENTIFIER(OP_DATALOAD, operation::dataload) \ + ON_OPCODE_IDENTIFIER(OP_DATALOADN, operation::dataloadn) \ + ON_OPCODE_IDENTIFIER(OP_DATASIZE, operation::datasize) \ + ON_OPCODE_IDENTIFIER(OP_DATACOPY, operation::datacopy) \ ON_OPCODE_UNDEFINED(0xec) \ ON_OPCODE_UNDEFINED(0xed) \ ON_OPCODE_UNDEFINED(0xee) \ ON_OPCODE_UNDEFINED(0xef) \ \ - ON_OPCODE_IDENTIFIER(OP_CREATE, create) \ - ON_OPCODE_IDENTIFIER(OP_CALL, call) \ - ON_OPCODE_IDENTIFIER(OP_CALLCODE, callcode) \ - ON_OPCODE_IDENTIFIER(OP_RETURN, return_) \ - ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, delegatecall) \ - ON_OPCODE_IDENTIFIER(OP_CREATE2, create2) \ + ON_OPCODE_IDENTIFIER(OP_CREATE, operation::create) \ + ON_OPCODE_IDENTIFIER(OP_CALL, operation::call) \ + ON_OPCODE_IDENTIFIER(OP_CALLCODE, operation::callcode) \ + ON_OPCODE_IDENTIFIER(OP_RETURN, operation::return_) \ + ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, operation::delegatecall) \ + ON_OPCODE_IDENTIFIER(OP_CREATE2, operation::create2) \ ON_OPCODE_UNDEFINED(0xf6) \ ON_OPCODE_UNDEFINED(0xf7) \ ON_OPCODE_UNDEFINED(0xf8) \ ON_OPCODE_UNDEFINED(0xf9) \ - ON_OPCODE_IDENTIFIER(OP_STATICCALL, staticcall) \ + ON_OPCODE_IDENTIFIER(OP_STATICCALL, operation::staticcall) \ ON_OPCODE_UNDEFINED(0xfb) \ ON_OPCODE_UNDEFINED(0xfc) \ - ON_OPCODE_IDENTIFIER(OP_REVERT, revert) \ - ON_OPCODE_IDENTIFIER(OP_INVALID, invalid) \ - ON_OPCODE_IDENTIFIER(OP_SELFDESTRUCT, selfdestruct) + ON_OPCODE_IDENTIFIER(OP_REVERT, operation::revert) \ + ON_OPCODE_IDENTIFIER(OP_INVALID, operation::invalid) \ + ON_OPCODE_IDENTIFIER(OP_SELFDESTRUCT, operation::selfdestruct) diff --git a/lib/evmone/tracing.cpp b/lib/evmone/tracing.cpp deleted file mode 100644 index 1b2e260e..00000000 --- a/lib/evmone/tracing.cpp +++ /dev/null @@ -1,239 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2021 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#include "tracing.hpp" -#include "execution_state.hpp" -#include "instructions_traits.hpp" -#include -#include -#include - -namespace evmone -{ -namespace -{ -std::string get_name(uint8_t opcode) -{ - // TODO: Create constexpr tables of names (maybe even per revision). - const auto name = instr::traits[opcode].name; - return (name != nullptr) ? name : "0x" + evmc::hex(opcode); -} - -/// @see create_histogram_tracer() -class HistogramTracer : public Tracer -{ - struct Context - { - const int32_t depth; - const uint8_t* const code; - uint32_t counts[256]{}; - - Context(int32_t _depth, const uint8_t* _code) noexcept : depth{_depth}, code{_code} {} - }; - - std::stack m_contexts; - std::ostream& m_out; - - void on_execution_start( - evmc_revision /*rev*/, const evmc_message& msg, bytes_view code) noexcept override - { - m_contexts.emplace(msg.depth, code.data()); - } - - void on_instruction_start(uint32_t pc, const intx::uint256* /*stack_top*/, int /*stack_height*/, - int64_t /*gas*/, const ExecutionState& /*state*/) noexcept override - { - auto& ctx = m_contexts.top(); - ++ctx.counts[ctx.code[pc]]; - } - - void on_execution_end(const evmc_result& /*result*/) noexcept override - { - const auto& ctx = m_contexts.top(); - - m_out << "--- # HISTOGRAM depth=" << ctx.depth << "\nopcode,count\n"; - for (size_t i = 0; i < std::size(ctx.counts); ++i) - { - if (ctx.counts[i] != 0) - m_out << get_name(static_cast(i)) << ',' << ctx.counts[i] << '\n'; - } - - m_contexts.pop(); - } - -public: - explicit HistogramTracer(std::ostream& out) noexcept : m_out{out} {} -}; - - -class InstructionTracer : public Tracer -{ - struct Context - { - const int32_t depth; - const uint8_t* const code; ///< Reference to the code being executed. - const int64_t start_gas; - - Context(int32_t d, const uint8_t* c, int64_t g) noexcept : depth{d}, code{c}, start_gas{g} - {} - }; - - std::stack m_contexts; - std::ostream& m_out; ///< Output stream. - - void output_stack(const intx::uint256* stack_top, int stack_height) - { - m_out << R"(,"stack":[)"; - const auto stack_end = stack_top + 1; - const auto stack_begin = stack_end - stack_height; - for (auto it = stack_begin; it != stack_end; ++it) - { - if (it != stack_begin) - m_out << ','; - m_out << R"("0x)" << to_string(*it, 16) << '"'; - } - m_out << ']'; - } - - void on_execution_start( - evmc_revision /*rev*/, const evmc_message& msg, bytes_view code) noexcept override - { - m_contexts.emplace(msg.depth, code.data(), msg.gas); - } - - void on_instruction_start(uint32_t pc, const intx::uint256* stack_top, int stack_height, - int64_t gas, const ExecutionState& state) noexcept override - { - const auto& ctx = m_contexts.top(); - - const auto opcode = ctx.code[pc]; - m_out << "{"; - m_out << R"("pc":)" << std::dec << pc; - m_out << R"(,"op":)" << std::dec << int{opcode}; - m_out << R"(,"gas":"0x)" << std::hex << gas << '"'; - m_out << R"(,"gasCost":"0x)" << std::hex << instr::gas_costs[state.rev][opcode] << '"'; - - // Full memory can be dumped as evmc::hex({state.memory.data(), state.memory.size()}), - // but this should not be done by default. Adding --tracing=+memory option would be nice. - m_out << R"(,"memSize":)" << std::dec << state.memory.size(); - -#if 1 // Dump memory if it was changed - static bool memory_was_changed = false; - if (memory_was_changed) { - m_out << R"(,"memory":")" << evmc::hex({state.memory.data(), state.memory.size()}) - << '"'; - memory_was_changed = false; - } - if (opcode == OP_MSTORE || opcode == OP_MSTORE8 || opcode == OP_CODECOPY || - opcode == OP_MSTORE16 || opcode == OP_MSTORE32 || opcode == OP_MSTORE64) - { - memory_was_changed = true; - } -#endif - - output_stack(stack_top, stack_height); - if (!state.return_data.empty()) - m_out << R"(,"returnData":"0x)" << evmc::hex(state.return_data) << '"'; - m_out << R"(,"depth":)" << std::dec << (ctx.depth + 1); - m_out << R"(,"refund":)" << std::dec << state.gas_refund; - m_out << R"(,"opName":")" << get_name(opcode) << '"'; - - m_out << "}\n"; - } - - void on_execution_end(const evmc_result& /*result*/) noexcept override { m_contexts.pop(); } - -public: - explicit InstructionTracer(std::ostream& out) noexcept : m_out{out} - { - m_out << std::dec; // Set number formatting to dec, JSON does not support other forms. - } -}; - - -class InstructionTracerFast : public Tracer -{ - -public: - explicit InstructionTracerFast(std::ostream& out) noexcept - : m_out(std::ofstream("trace.bin", std::ios::out | std::ios::binary)) - { - (void)out; - } - -private: - void on_execution_start([[maybe_unused]] evmc_revision rev, - [[maybe_unused]] const evmc_message& msg, bytes_view code) noexcept override - { - m_code = code; - } - - void on_execution_end([[maybe_unused]] const evmc_result& result) noexcept override - { - } - - void on_instruction_start(uint32_t pc, - const intx::uint256* stack_top, - int stack_height, - int64_t gas, - [[maybe_unused]] const ExecutionState& state) noexcept override - { - - static bool first = true; - const auto opcode = m_code[pc]; - auto& traits = instr::traits[m_last_opcode]; - [[maybe_unused]] int push_num = traits.stack_height_required + traits.stack_height_change; - if (m_last_opcode >= OP_DUP1 && m_last_opcode <= OP_DUP16) { - push_num = 1; - } else if (m_last_opcode >= OP_SWAP1 && m_last_opcode <= OP_SWAP16) { - push_num = 0; - } - - assert((push_num & ~1) == 0); - - if (!first) - { - if ( stack_height != 0) - { - m_out.write((const char*)&stack_top[0], stack_top->num_bits/8); - } - else - { - constexpr const char s[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - m_out.write(s, 32); - } - } else { - first = false; - } - m_out.write((char*)&pc, sizeof(uint32_t)); - m_out.write((char*)&opcode, sizeof(char)); - m_out.write((char*)&gas, sizeof(uint32_t)); - m_last_opcode = (Opcode)opcode; - } - -private: - bytes_view m_code; - std::ofstream m_out; - Opcode m_last_opcode{OP_INVALID}; -}; - -} // namespace - -std::unique_ptr create_histogram_tracer(std::ostream& out) -{ - return std::make_unique(out); -} - -std::unique_ptr create_instruction_tracer(std::ostream& out) -{ - return std::make_unique(out); -} - -std::unique_ptr create_instruction_fast_tracer(std::ostream& out) -{ - return std::make_unique(out); -} - -} // namespace evmone diff --git a/lib/evmone/tracing.hpp b/lib/evmone/tracing.hpp deleted file mode 100644 index 9fed80d9..00000000 --- a/lib/evmone/tracing.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2021 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace evmone -{ -using bytes_view = std::basic_string_view; - -class ExecutionState; - -class Tracer -{ - friend class VM; // Has access the m_next_tracer to traverse the list forward. - std::unique_ptr m_next_tracer; - -public: - virtual ~Tracer() = default; - - void notify_execution_start( // NOLINT(misc-no-recursion) - evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept - { - on_execution_start(rev, msg, code); - if (m_next_tracer) - m_next_tracer->notify_execution_start(rev, msg, code); - } - - void notify_execution_end(const evmc_result& result) noexcept // NOLINT(misc-no-recursion) - { - on_execution_end(result); - if (m_next_tracer) - m_next_tracer->notify_execution_end(result); - } - - void notify_instruction_start( // NOLINT(misc-no-recursion) - uint32_t pc, intx::uint256* stack_top, int stack_height, int64_t gas, - const ExecutionState& state) noexcept - { - on_instruction_start(pc, stack_top, stack_height, gas, state); - if (m_next_tracer) - m_next_tracer->notify_instruction_start(pc, stack_top, stack_height, gas, state); - } - -private: - virtual void on_execution_start( - evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept = 0; - virtual void on_instruction_start(uint32_t pc, const intx::uint256* stack_top, int stack_height, - int64_t gas, const ExecutionState& state) noexcept = 0; - virtual void on_execution_end(const evmc_result& result) noexcept = 0; -}; - -/// Creates the "histogram" tracer which counts occurrences of individual opcodes during execution -/// and reports this data in CSV format. -/// -/// @param out Report output stream. -/// @return Histogram tracer object. -EVMC_EXPORT std::unique_ptr create_histogram_tracer(std::ostream& out); - -EVMC_EXPORT std::unique_ptr create_instruction_tracer(std::ostream& out); - -EVMC_EXPORT std::unique_ptr create_instruction_fast_tracer(std::ostream& out); - -} // namespace evmone diff --git a/lib/evmone/vm.cpp b/lib/evmone/vm.cpp index 6cab7a9a..83cf3733 100644 --- a/lib/evmone/vm.cpp +++ b/lib/evmone/vm.cpp @@ -48,21 +48,6 @@ evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, return EVMC_SET_OPTION_INVALID_NAME; #endif } - else if (name == "trace") - { - vm.add_tracer(create_instruction_tracer(std::clog)); - return EVMC_SET_OPTION_SUCCESS; - } - else if (name == "trace-bin") - { - vm.add_tracer(create_instruction_fast_tracer(std::cerr)); - return EVMC_SET_OPTION_SUCCESS; - } - else if (name == "histogram") - { - vm.add_tracer(create_histogram_tracer(std::clog)); - return EVMC_SET_OPTION_SUCCESS; - } return EVMC_SET_OPTION_INVALID_NAME; } diff --git a/lib/evmone/vm.hpp b/lib/evmone/vm.hpp index ebce69e0..79318754 100644 --- a/lib/evmone/vm.hpp +++ b/lib/evmone/vm.hpp @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include "tracing.hpp" #include #if defined(_MSC_VER) && !defined(__clang__) @@ -20,21 +19,7 @@ class VM : public evmc_vm public: bool cgoto = EVMONE_CGOTO_SUPPORTED; -private: - std::unique_ptr m_first_tracer; - public: inline constexpr VM() noexcept; - - void add_tracer(std::unique_ptr tracer) noexcept - { - // Find the first empty unique_ptr and assign the new tracer to it. - auto* end = &m_first_tracer; - while (*end) - end = &(*end)->m_next_tracer; - *end = std::move(tracer); - } - - [[nodiscard]] Tracer* get_tracer() const noexcept { return m_first_tracer.get(); } }; } // namespace evmone From 7409a66d74ac31e17e00df750922af9bc3c5a45a Mon Sep 17 00:00:00 2001 From: akokoshn Date: Fri, 14 Jun 2024 22:53:44 +0300 Subject: [PATCH 2/7] Remove assigner_interace --- lib/assigner/CMakeLists.txt | 6 +- .../include/nil/blueprint/assigner.hpp | 31 ++++++++ .../nil/blueprint/assigner_interface.hpp | 35 --------- .../include/nil/blueprint/zkevm_word.hpp | 3 +- lib/assigner/include/vm_host.h | 8 +- lib/assigner/test/assigner_test.cpp | 23 +++--- lib/evmone/baseline.hpp | 29 ------- lib/evmone/execution_state.hpp | 9 ++- lib/evmone/vm.cpp | 76 ------------------- lib/evmone/vm.hpp | 25 ------ 10 files changed, 53 insertions(+), 192 deletions(-) delete mode 100644 lib/assigner/include/nil/blueprint/assigner_interface.hpp delete mode 100644 lib/evmone/vm.cpp delete mode 100644 lib/evmone/vm.hpp diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index 8ba9939d..e50fb79b 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -9,7 +9,6 @@ set(evmone_sources ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline_instruction_table.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.cpp ) add_library(${PROJECT_NAME} STATIC ${evmone_sources}) @@ -35,8 +34,6 @@ set_target_properties( SOVERSION ${PROJECT_SOVERSION} ) -set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.cpp PROPERTIES COMPILE_DEFINITIONS PROJECT_VERSION="${PROJECT_VERSION}") - # Install assigner headers install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) @@ -44,8 +41,7 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ install(TARGETS ${PROJECT_NAME} EXPORT assignerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) # Install evmone headers -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/vm.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/execution_state.hpp +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/execution_state.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/instructions_opcodes.hpp diff --git a/lib/assigner/include/nil/blueprint/assigner.hpp b/lib/assigner/include/nil/blueprint/assigner.hpp index c94e1f97..3766079b 100644 --- a/lib/assigner/include/nil/blueprint/assigner.hpp +++ b/lib/assigner/include/nil/blueprint/assigner.hpp @@ -13,6 +13,9 @@ #include #include +#include +#include + namespace nil { namespace blueprint { @@ -25,6 +28,34 @@ namespace nil { std::vector> &m_assignments; }; + + template + static evmc::Result evaluate(const evmc_host_interface* host, evmc_host_context* ctx, + evmc_revision rev, const evmc_message* msg, const uint8_t* code_ptr, size_t code_size, + std::shared_ptr> assigner) { + const evmone::bytes_view container{code_ptr, code_size}; + const auto code_analysis = evmone::baseline::analyze(rev, container); + const auto data = code_analysis.eof_header.get_data(container); + auto state = std::make_unique>(*msg, rev, *host, ctx, container, data, assigner); + + state->analysis.baseline = &code_analysis; // Assign code analysis for instruction implementations. + const auto code = code_analysis.executable_code; + + int64_t gas = msg->gas; + + const auto& cost_table = evmone::baseline::get_baseline_cost_table(state->rev, code_analysis.eof_header.version); + + gas = evmone::baseline::dispatch(cost_table, *state, msg->gas, code.data()); + + const auto gas_left = (state->status == EVMC_SUCCESS || state->status == EVMC_REVERT) ? gas : 0; + const auto gas_refund = (state->status == EVMC_SUCCESS) ? state->gas_refund : 0; + + assert(state->output_size != 0 || state->output_offset == 0); + const auto evmc_result = evmc::make_result(state->status, gas_left, gas_refund, + state->output_size != 0 ? &state->memory[state->output_offset] : nullptr, state->output_size); + return evmc::Result{evmc_result}; + } + } // namespace blueprint } // namespace nil diff --git a/lib/assigner/include/nil/blueprint/assigner_interface.hpp b/lib/assigner/include/nil/blueprint/assigner_interface.hpp deleted file mode 100644 index 8878ac4b..00000000 --- a/lib/assigner/include/nil/blueprint/assigner_interface.hpp +++ /dev/null @@ -1,35 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) Nil Foundation and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. -//---------------------------------------------------------------------------// - -#ifndef EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ -#define EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ - -#include -#include -#include -#include - -namespace nil { - namespace blueprint { - - template - static evmc::Result evaluate(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx, - evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size, - std::shared_ptr> assigner) { - auto vm = static_cast(c_vm); - const evmone::bytes_view container{code, code_size}; - const auto code_analysis = evmone::baseline::analyze(rev, container); - const auto data = code_analysis.eof_header.get_data(container); - auto state = std::make_unique>(*msg, rev, *host, ctx, container, data, assigner); - - auto res = execute(*vm, msg->gas, *state, code_analysis); - return evmc::Result{res}; - } - } // namespace blueprint -} // namespace nil - -#endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ASSIGNER_INTERACE_HPP_ diff --git a/lib/assigner/include/nil/blueprint/zkevm_word.hpp b/lib/assigner/include/nil/blueprint/zkevm_word.hpp index 9b614636..5c1725fd 100644 --- a/lib/assigner/include/nil/blueprint/zkevm_word.hpp +++ b/lib/assigner/include/nil/blueprint/zkevm_word.hpp @@ -12,7 +12,8 @@ #include #include -#include +#include +#include #include #include #include diff --git a/lib/assigner/include/vm_host.h b/lib/assigner/include/vm_host.h index 6db75472..344e27d8 100644 --- a/lib/assigner/include/vm_host.h +++ b/lib/assigner/include/vm_host.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include using namespace evmc::literals; @@ -219,7 +219,6 @@ class VMHost : public evmc::Host std::shared_ptr> assigner; evmc::Result handle_call(const evmc_message& msg) { - evmc_vm * vm = evmc_create_evmone(); auto sender_iter = accounts.find(msg.sender); if (sender_iter == accounts.end()) { @@ -246,7 +245,7 @@ class VMHost : public evmc::Host return evmc::Result{EVMC_SUCCESS, msg.gas, 0, msg.input_data, msg.input_size}; } // TODO: handle precompiled contracts - evmc::Result res = nil::blueprint::evaluate(vm, &get_interface(), to_context(), + evmc::Result res = nil::blueprint::evaluate(&get_interface(), to_context(), EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size(), assigner); return res; } @@ -263,13 +262,12 @@ class VMHost : public evmc::Host { return evmc::Result{EVMC_SUCCESS, msg.gas, 0, new_contract_address}; } - evmc::VM vm{evmc_create_evmone()}; evmc_message init_msg(msg); init_msg.kind = EVMC_CALL; init_msg.recipient = new_contract_address; init_msg.sender = msg.sender; init_msg.input_size = 0; - evmc::Result res = nil::blueprint::evaluate(vm.get_raw_pointer(), &get_interface(), to_context(), + evmc::Result res = nil::blueprint::evaluate(&get_interface(), to_context(), EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size, assigner); if (res.status_code == EVMC_SUCCESS) { diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index eb6da1b9..3e56da6a 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -33,9 +33,6 @@ class AssignerTest : public testing::Test assigner_ptr = std::make_shared>(assignments); - vm = evmc_create_evmone(); - - const uint8_t input[] = "Hello World!"; const evmc_uint256be value = {{1, 0}}; const evmc_address sender_addr = {{0, 1, 2}}; @@ -77,7 +74,6 @@ class AssignerTest : public testing::Test static std::vector> assignments; static const struct evmc_host_interface* host_interface; static struct evmc_host_context* ctx; - static struct evmc_vm* vm; static evmc_revision rev; static struct evmc_message msg; }; @@ -88,7 +84,6 @@ std::vector> AssignerTest::assignments; const struct evmc_host_interface* AssignerTest::host_interface; struct evmc_host_context* AssignerTest::ctx; -struct evmc_vm* AssignerTest::vm; evmc_revision AssignerTest::rev = {}; struct evmc_message AssignerTest::msg; @@ -142,7 +137,7 @@ TEST_F(AssignerTest, mul) { evmone::OP_MUL, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(0, 0), 8); EXPECT_EQ(assignments[0].witness(0, 1), 4); } @@ -156,7 +151,7 @@ TEST_F(AssignerTest, callvalue_calldataload) index, evmone::OP_CALLDATALOAD, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); //EXPECT_EQ(assignments[0].witness(1, 0), nil::blueprint::to_field(msg.value)); EXPECT_EQ(assignments[0].witness(1, 1), index); } @@ -169,7 +164,7 @@ TEST_F(AssignerTest, DISABLED_dataload) { index, evmone::OP_DATALOAD, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(1, 2), index); } @@ -187,7 +182,7 @@ TEST_F(AssignerTest, mstore_load) index, evmone::OP_MLOAD, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(2, 0), value); EXPECT_EQ(assignments[0].witness(2, 1), index); EXPECT_EQ(assignments[0].witness(2, 2), value); @@ -207,7 +202,7 @@ TEST_F(AssignerTest, sstore_load) key, evmone::OP_SLOAD, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(3, 0), value); EXPECT_EQ(assignments[0].witness(3, 1), key); EXPECT_EQ(assignments[0].witness(3, 2), value); @@ -227,7 +222,7 @@ TEST_F(AssignerTest, DISABLED_tstore_load) { key, evmone::OP_TLOAD, }; - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); EXPECT_EQ(assignments[0].witness(4, 0), value); EXPECT_EQ(assignments[0].witness(4, 1), key); EXPECT_EQ(assignments[0].witness(4, 2), value); @@ -284,7 +279,7 @@ TEST_F(AssignerTest, create) { // Code is in the last 13 bytes of the container code.insert(code.begin() + static_cast(push13_idx) + 1, contract_code.begin(), contract_code.end()); - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); // Check stored witnesses of MSTORE instruction at depth 1 EXPECT_EQ(assignments[1].witness(2, 1), 0); EXPECT_EQ(assignments[1].witness(2, 0), 0xFFFFFFFF); @@ -351,7 +346,7 @@ TEST_F(AssignerTest, call) { // Code is in the last 13 bytes of the container code.insert(code.begin() + static_cast(push17_idx) + 1, contract_code.begin(), contract_code.end()); - nil::blueprint::evaluate(vm, host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); // Check stored witness of CALLDATALOAD instruction at depth 1 EXPECT_EQ(assignments[1].witness(1, 1), 0); } diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index 8ac703aa..fbd1c324 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -7,7 +7,6 @@ #include "baseline_instruction_table.hpp" #include "execution_state.hpp" #include "instructions.hpp" -#include "vm.hpp" #include #include @@ -253,33 +252,5 @@ int64_t dispatch(const CostTable& cost_table, ExecutionState //intx::unreachable(); } -/// Executes in Baseline interpreter using EVMC-compatible parameters. -static evmc_result execute(evmc_vm* c_vm, 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 { - const auto result = evmc::make_result(EVMC_SUCCESS, msg->gas, msg->gas, nullptr, 0); - return result; -} - -/// Executes in Baseline interpreter on the given external and initialized state. -template -static evmc_result execute(const VM&, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept { - state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. - - const auto code = analysis.executable_code; - - const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version); - - gas = dispatch(cost_table, state, gas, code.data()); - - const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; - const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; - - assert(state.output_size != 0 || state.output_offset == 0); - const auto result = evmc::make_result(state.status, gas_left, gas_refund, - state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size); - - return result; -} - } // namespace baseline } // namespace evmone diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index 6e774584..e3daf68d 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -7,9 +7,15 @@ #include #include -#include #include +namespace nil { + namespace blueprint { + template + struct assigner; + } +} + namespace evmone { namespace baseline @@ -113,7 +119,6 @@ class Memory void clear() noexcept { m_size = 0; } }; - /// Generic execution state for generic instructions implementations. // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) template diff --git a/lib/evmone/vm.cpp b/lib/evmone/vm.cpp deleted file mode 100644 index 83cf3733..00000000 --- a/lib/evmone/vm.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2018 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -/// @file -/// EVMC instance (class VM) and entry point of evmone is defined here. - -#include "vm.hpp" -#include "baseline.hpp" -#include -#include -#include - -namespace evmone -{ -namespace -{ -void destroy(evmc_vm* vm) noexcept -{ - assert(vm != nullptr); - delete static_cast(vm); -} - -constexpr evmc_capabilities_flagset get_capabilities(evmc_vm* /*vm*/) noexcept -{ - return EVMC_CAPABILITY_EVM1; -} - -evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, - [[maybe_unused]] char const* c_value) noexcept -{ - const auto name = (c_name != nullptr) ? std::string_view{c_name} : std::string_view{}; -#if EVMONE_CGOTO_SUPPORTED - const auto value = (c_value != nullptr) ? std::string_view{c_value} : std::string_view{}; -#endif - auto& vm = *static_cast(c_vm); - - if (name == "cgoto") - { -#if EVMONE_CGOTO_SUPPORTED - if (value == "no") - { - vm.cgoto = false; - return EVMC_SET_OPTION_SUCCESS; - } - return EVMC_SET_OPTION_INVALID_VALUE; -#else - return EVMC_SET_OPTION_INVALID_NAME; -#endif - } - return EVMC_SET_OPTION_INVALID_NAME; -} - -} // namespace - - -inline constexpr VM::VM() noexcept - : evmc_vm{ - EVMC_ABI_VERSION, - "evmone", - PROJECT_VERSION, - evmone::destroy, - evmone::baseline::execute, - evmone::get_capabilities, - evmone::set_option, - } -{} - -} // namespace evmone - -extern "C" { -EVMC_EXPORT evmc_vm* evmc_create_evmone() noexcept -{ - return new evmone::VM{}; -} -} diff --git a/lib/evmone/vm.hpp b/lib/evmone/vm.hpp deleted file mode 100644 index 79318754..00000000 --- a/lib/evmone/vm.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2021 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#include - -#if defined(_MSC_VER) && !defined(__clang__) -#define EVMONE_CGOTO_SUPPORTED 0 -#else -#define EVMONE_CGOTO_SUPPORTED 0 -#endif - -namespace evmone -{ -/// The evmone EVMC instance. -class VM : public evmc_vm -{ -public: - bool cgoto = EVMONE_CGOTO_SUPPORTED; - -public: - inline constexpr VM() noexcept; -}; -} // namespace evmone From 852470fca4a53b924fb5e195182141157c5eb10c Mon Sep 17 00:00:00 2001 From: akokoshn Date: Sat, 15 Jun 2024 19:17:05 +0300 Subject: [PATCH 3/7] Add zkevm_word tests --- .../include/nil/blueprint/zkevm_word.hpp | 31 ++------- lib/assigner/test/assigner_test.cpp | 69 +++++++++++++------ 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/lib/assigner/include/nil/blueprint/zkevm_word.hpp b/lib/assigner/include/nil/blueprint/zkevm_word.hpp index 5c1725fd..1704dee7 100644 --- a/lib/assigner/include/nil/blueprint/zkevm_word.hpp +++ b/lib/assigner/include/nil/blueprint/zkevm_word.hpp @@ -65,33 +65,6 @@ namespace nil { value = intx::be::unsafe::load(data); } - zkevm_word(column_type_t column_type, size_t column_idx, size_t row_idx, const assignment& tbl) { - typename BlueprintFieldType::value_type field_val; - switch (column_type) { - case column_type_t::witness: - field_val = tbl.witness(column_idx, row_idx); - break; - case column_type_t::constant: - field_val = tbl.constant(column_idx, row_idx); - break; - case column_type_t::public_input: - field_val = tbl.public_input(column_idx, row_idx); - break; - }; - auto field_container = nil::crypto3::marshalling::types::field_element(field_val); - evmc::uint256be data; - auto write_iter = data.bytes; - field_container.write(write_iter, field_container.length()); - value = value = intx::be::load(data); - } - - void put_into_assignment_table(column_type_t column_type, size_t column_idx, size_t row_idx, assignment& tbl) const { - auto field_container = nil::crypto3::marshalling::types::field_element(); - auto data = intx::be::store(value); - auto read_iter = data.bytes; - field_container.read(read_iter, field_container.length()); - } - // operators zkevm_word operator+(const zkevm_word& other) const { return zkevm_word(value + other.value); @@ -198,6 +171,10 @@ namespace nil { return static_cast(value[i]); } + ethash::hash256 to_hash() const { + return intx::be::store(value); + } + evmc::address to_address() const { return intx::be::trunc(value); } diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index 3e56da6a..ab25239e 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -87,45 +87,72 @@ struct evmc_host_context* AssignerTest::ctx; evmc_revision AssignerTest::rev = {}; struct evmc_message AssignerTest::msg; -/*std::string to_string(const uint8_t* bytes, size_t len) { - std::ostringstream oss; +inline void check_eq(const uint8_t* l, const uint8_t* r, size_t len) { for (int i = 0; i < len; i++) { - oss << bytes[i]; + EXPECT_EQ(l[i], r[i]); } - return oss.str(); } -TEST_F(AssignerTest, conversions_uint256be_to_field) +TEST_F(AssignerTest, conversions_uint256be_to_zkevm_word) { evmc::uint256be uint256be_number; uint256be_number.bytes[2] = 10; // Some big number, 10 << 128 - // conversion to field - auto field = nil::blueprint::to_field(uint256be_number); + // conversion to zkevm_word + auto tmp = nil::blueprint::zkevm_word(uint256be_number); // conversion back to uint256be - evmc::uint256be uint256be_result = nil::blueprint::to_uint256be(field); + evmc::uint256be uint256be_result = tmp.to_uint256be(); // check if same - EXPECT_EQ(to_string(uint256be_number.bytes, 32), to_string(uint256be_result.bytes, 32)); + check_eq(uint256be_number.bytes, uint256be_result.bytes, 32); } -TEST_F(AssignerTest, conversions_address_to_field) +TEST_F(AssignerTest, conversions_address_to_zkevm_word) { evmc::address address; address.bytes[19] = 10; - // conversion to field - auto field = nil::blueprint::to_field(address); - // conversion back to uint256be - evmc::address address_result = nil::blueprint::to_address(field); + // conversion to zkevm_word + auto tmp = nil::blueprint::zkevm_word(address); + // conversion back to address + evmc::address address_result = tmp.to_address(); + // check if same + check_eq(address.bytes, address_result.bytes, 20); +} + +TEST_F(AssignerTest, conversions_hash_to_zkevm_word) +{ + ethash::hash256 hash; + hash.bytes[2] = 10; + // conversion to zkevm_word + auto tmp = nil::blueprint::zkevm_word(hash); + // conversion back to address + ethash::hash256 hash_result = tmp.to_hash(); // check if same - EXPECT_EQ(to_string(address.bytes, 20), to_string(address_result.bytes, 20)); + check_eq(hash.bytes, hash_result.bytes, 32); } -TEST_F(AssignerTest, conversions_field_to_uint64) +TEST_F(AssignerTest, conversions_uint64_to_zkevm_word) { - BlueprintFieldType::value_type field = 10; - // conversion to uint64_t - auto uint64_number = nil::blueprint::to_uint64(field); - EXPECT_EQ(uint64_number, 10); -}*/ + uint64_t number = std::numeric_limits::max(); + // conversion to zkevm_word + auto tmp = nil::blueprint::zkevm_word(number); + // conversion back to address + auto number_result = tmp.to_uint64(); + // check if same + EXPECT_EQ(number_result, number); +} + +TEST_F(AssignerTest, load_store_zkevm_word) +{ + uint64_t number = std::numeric_limits::max(); + + nil::blueprint::zkevm_word tmp; + // load data to the lase 64 bits + tmp.load_partial_data(reinterpret_cast(&number), 8, 0); + uint64_t number_result; + // store back to the uint64_t variable + tmp.store(reinterpret_cast(&number_result)); + // check if same + EXPECT_EQ(number_result, number); +} TEST_F(AssignerTest, mul) { From 15f9a727b9fdc9a8bb54013336fd8d1c19b447fc Mon Sep 17 00:00:00 2001 From: akokoshn Date: Mon, 17 Jun 2024 23:38:03 +0300 Subject: [PATCH 4/7] Remove useless files --- include/evmone/evmone.h | 21 -- .../include/{nil/blueprint => }/assigner.hpp | 0 .../include/{vm_host.h => vm_host.hpp} | 5 +- .../{nil/blueprint => }/zkevm_word.hpp | 0 lib/assigner/test/assigner_test.cpp | 7 +- lib/evmone/baseline.hpp | 8 +- lib/evmone/cpu_check.cpp | 29 -- lib/evmone/eof.hpp | 1 + lib/evmone/execution_state.hpp | 2 +- lib/evmone/fprinter.h | 255 ------------------ lib/evmone/opcodes_helpers.h | 40 --- 11 files changed, 10 insertions(+), 358 deletions(-) delete mode 100644 include/evmone/evmone.h rename lib/assigner/include/{nil/blueprint => }/assigner.hpp (100%) rename lib/assigner/include/{vm_host.h => vm_host.hpp} (99%) rename lib/assigner/include/{nil/blueprint => }/zkevm_word.hpp (100%) delete mode 100644 lib/evmone/cpu_check.cpp delete mode 100644 lib/evmone/fprinter.h delete mode 100644 lib/evmone/opcodes_helpers.h diff --git a/include/evmone/evmone.h b/include/evmone/evmone.h deleted file mode 100644 index abf64c35..00000000 --- a/include/evmone/evmone.h +++ /dev/null @@ -1,21 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2018-2019 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef EVMONE_H -#define EVMONE_H - -#include -#include - -#if __cplusplus -extern "C" { -#endif - -EVMC_EXPORT struct evmc_vm* evmc_create_evmone(void) EVMC_NOEXCEPT; - -#if __cplusplus -} -#endif - -#endif // EVMONE_H diff --git a/lib/assigner/include/nil/blueprint/assigner.hpp b/lib/assigner/include/assigner.hpp similarity index 100% rename from lib/assigner/include/nil/blueprint/assigner.hpp rename to lib/assigner/include/assigner.hpp diff --git a/lib/assigner/include/vm_host.h b/lib/assigner/include/vm_host.hpp similarity index 99% rename from lib/assigner/include/vm_host.h rename to lib/assigner/include/vm_host.hpp index 344e27d8..45808b75 100644 --- a/lib/assigner/include/vm_host.h +++ b/lib/assigner/include/vm_host.hpp @@ -4,7 +4,6 @@ // Based on example host #include -#include #include #include @@ -12,8 +11,8 @@ #include #include -#include -#include +#include +#include using namespace evmc::literals; diff --git a/lib/assigner/include/nil/blueprint/zkevm_word.hpp b/lib/assigner/include/zkevm_word.hpp similarity index 100% rename from lib/assigner/include/nil/blueprint/zkevm_word.hpp rename to lib/assigner/include/zkevm_word.hpp diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index ab25239e..c0b5d5dc 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -1,13 +1,12 @@ #include -#include +#include #include #include -#include #include -#include "instructions_opcodes.hpp" -#include "vm_host.h" +#include +#include #include diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index fbd1c324..011f7d7e 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -205,14 +205,13 @@ template state.status = status; return {nullptr, pos.stack_top}; } - //const auto new_pos = invoke(instr::core::operation::template push<1>, pos, gas, state); code_iterator new_pos = pos.code_it; switch (op) { #undef ON_OPCODE_IDENTIFIER -#define ON_OPCODE_IDENTIFIER(OPCODE, IDENTIFIER) \ -case OPCODE: \ - ASM_COMMENT(OPCODE); \ +#define ON_OPCODE_IDENTIFIER(OPCODE, IDENTIFIER) \ +case OPCODE: \ + ASM_COMMENT(OPCODE); \ new_pos = invoke(instr::core::IDENTIFIER, pos, gas, state); \ break; MAP_OPCODES @@ -249,7 +248,6 @@ int64_t dispatch(const CostTable& cost_table, ExecutionState position = next; } } - //intx::unreachable(); } } // namespace baseline diff --git a/lib/evmone/cpu_check.cpp b/lib/evmone/cpu_check.cpp deleted file mode 100644 index 8e60630f..00000000 --- a/lib/evmone/cpu_check.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -#define STRINGIFY_HELPER(X) #X -#define STRINGIFY(X) STRINGIFY_HELPER(X) - -#if defined(__GNUC__) && __GNUC__ >= 12 -#define CPU_FEATURE "x86-64-v" STRINGIFY(EVMONE_X86_64_ARCH_LEVEL) -#else -// Clang 16 and GCC 11 does not support architecture levels in __builtin_cpu_supports(). -// Use approximations. -#if EVMONE_X86_64_ARCH_LEVEL == 2 -#define CPU_FEATURE "sse4.2" -#endif -#endif - -#ifndef CPU_FEATURE -#error "EVMONE_X86_64_ARCH_LEVEL: Unsupported x86-64 architecture level" -#endif - -#include -#include - -static bool cpu_check = []() noexcept { - if (!__builtin_cpu_supports(CPU_FEATURE)) - { - (void)std::fputs("CPU does not support " CPU_FEATURE "\n", stderr); - std::abort(); - } - return false; -}(); diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index 75fec2d3..361cf76c 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include #include diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index e3daf68d..b125f3c7 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include namespace nil { namespace blueprint { diff --git a/lib/evmone/fprinter.h b/lib/evmone/fprinter.h deleted file mode 100644 index 6a9bf7bf..00000000 --- a/lib/evmone/fprinter.h +++ /dev/null @@ -1,255 +0,0 @@ -#ifndef EVMONE_FPRINTER_H -#define EVMONE_FPRINTER_H - -#include -#include -#include -#include -#include -#include - - -struct PrintfHandler -{ - virtual uint64_t GetArgNumber() = 0; - virtual const char* GetArgString() = 0; - virtual void Puts(const char* data) = 0; - virtual void Putc(char ch) = 0; -}; - - -class PrintfParser -{ -public: - PrintfParser(PrintfHandler* handler) : handler_(handler) {} - - static int Parse(const char* format, PrintfHandler& handler) - { - return PrintfParser(&handler).Parse(format); - } - - int Parse(const char* format) - { - flags_ = {}; - fmt_ = format; - unsigned length = 0; - - for (char ch = FetchChar(); ch != '\0'; ch = FetchChar()) - { - if (ch != '%') - { - handler_->Putc(ch); - continue; - } - while (true) - { - ch = FetchChar(); - switch (ch) - { - case '-': - flags_.left_justify = true; - continue; - case ' ': - case '+': - flags_.sign_symbol = ch; - continue; - case '#': - flags_.hash_sign = true; - continue; - } - break; - } - if (ch == '0') - { - flags_.padding_char = '0'; - ch = FetchChar(); - } - - if (std::isdigit(ch)) - { - char* end; - flags_.width = - static_cast(std::strtoul(fmt_ - 1, &end, 10)); - fmt_ = end; - ch = FetchChar(); - } - - if (ch == '.') - { - return raise_error("unsupported"); - } - for (; ch == 'l'; ch = FetchChar()) - { - length++; - } - if (length > 2) - { - return raise_error("Too many length symbols"); - } - - switch (ch) - { - case 'i': - case 'd': - if (length > 0) - { - process_integer(); - } - else - { - process_integer(); - } - break; - case 'u': - if (length > 0) - { - process_integer(); - } - else - { - process_integer(); - } - break; - case 'p': - flags_.padding_char = '0'; - flags_.hash_sign = true; - length = 2; - [[fallthrough]]; - case 'x': - flags_.base = 16; - if (length > 0) - { - process_integer(); - } - else - { - process_integer(); - } - break; - case 's': - { - auto str = handler_->GetArgString(); - handler_->Puts(str); - break; - } - default: - return raise_error("Unsupported specifier"); - } - } - return 0; - } - - const char* GetErrorMsg() const { return error_msg_; } - -private: - char FetchChar() { return *fmt_++; } - - template - int process_integer() - { - static const char digits[] = "0123456789abcdef0123456789ABCDEF"; - static constexpr unsigned buf_len = 64; - char buffer[buf_len]; - auto val = static_cast(handler_->GetArgNumber()); - std::pair prefix = {"", 0}; - - if (val != 0 && flags_.hash_sign) - { - if (flags_.base == 8) - { - prefix = {"0", 1}; - } - else if (flags_.base == 16) - { - prefix = {"0x", 2}; - } - } - - char sign_char = flags_.sign_symbol; - if constexpr (std::is_signed_v) - { - if (val < 0) - { - sign_char = '-'; - val = -val; - } - } - - buffer[buf_len - 1] = '\0'; - char* p_end = &buffer[buf_len - 2]; - char* p = p_end; - do - { - *p-- = digits[(val % flags_.base) + flags_.capitals]; - val /= flags_.base; - } while (val != 0); - - using WidthType = decltype(flags_.width); - auto width_diff = p_end - p; - assert(width_diff < std::numeric_limits::max()); - flags_.width -= static_cast(width_diff); - if (sign_char != 0) - { - flags_.width--; - } - flags_.width -= static_cast(prefix.second); - - if (flags_.padding_char == ' ' && !flags_.left_justify) - { - while (--flags_.width >= 0) - { - handler_->Putc(' '); - } - } - if (sign_char) - { - handler_->Putc(sign_char); - } - if (prefix.second) - { - handler_->Puts(prefix.first); - } - if (flags_.padding_char == '0') - { - while (--flags_.width >= 0) - { - handler_->Putc('0'); - } - } - handler_->Puts(p + 1); - - if (flags_.left_justify) - { - while (--flags_.width >= 0) - { - handler_->Putc(' '); - } - } - - return 0; - } - - int raise_error(const char* msg) - { - error_msg_ = msg; - return -1; - } - -private: - struct - { - int16_t width = 0; - char sign_symbol = 0; - char padding_char = ' '; - uint8_t base = 10; - uint8_t capitals = 0; - bool left_justify = false; - bool hash_sign = false; - } flags_; - const char* error_msg_{nullptr}; - const char* fmt_{nullptr}; - PrintfHandler* handler_{nullptr}; -}; - - -#endif // EVMONE_FPRINTER_H diff --git a/lib/evmone/opcodes_helpers.h b/lib/evmone/opcodes_helpers.h deleted file mode 100644 index 68f97ee4..00000000 --- a/lib/evmone/opcodes_helpers.h +++ /dev/null @@ -1,40 +0,0 @@ -// evmone: Fast Ethereum Virtual Machine implementation -// Copyright 2019 The evmone Authors. -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#define ANY_SMALL_PUSH \ - OP_PUSH1: \ - case OP_PUSH2: \ - case OP_PUSH3: \ - case OP_PUSH4: \ - case OP_PUSH5: \ - case OP_PUSH6: \ - case OP_PUSH7: \ - case OP_PUSH8 - -#define ANY_LARGE_PUSH \ - OP_PUSH9: \ - case OP_PUSH10: \ - case OP_PUSH11: \ - case OP_PUSH12: \ - case OP_PUSH13: \ - case OP_PUSH14: \ - case OP_PUSH15: \ - case OP_PUSH16: \ - case OP_PUSH17: \ - case OP_PUSH18: \ - case OP_PUSH19: \ - case OP_PUSH20: \ - case OP_PUSH21: \ - case OP_PUSH22: \ - case OP_PUSH23: \ - case OP_PUSH24: \ - case OP_PUSH25: \ - case OP_PUSH26: \ - case OP_PUSH27: \ - case OP_PUSH28: \ - case OP_PUSH29: \ - case OP_PUSH30: \ - case OP_PUSH31: \ - case OP_PUSH32 From aa5b9e1565767a8cd2c2323adcb0732c48e8879e Mon Sep 17 00:00:00 2001 From: akokoshn Date: Tue, 18 Jun 2024 10:37:40 +0300 Subject: [PATCH 5/7] Move directory evmone ot assigner --- lib/assigner/CMakeLists.txt | 16 ++++++++-------- lib/{ => assigner}/evmone/baseline.cpp | 0 lib/{ => assigner}/evmone/baseline.hpp | 0 .../evmone/baseline_instruction_table.cpp | 0 .../evmone/baseline_instruction_table.hpp | 0 lib/{ => assigner}/evmone/eof.cpp | 0 lib/{ => assigner}/evmone/eof.hpp | 0 lib/{ => assigner}/evmone/execution_state.hpp | 0 lib/{ => assigner}/evmone/instructions.hpp | 0 .../evmone/instructions_opcodes.hpp | 0 .../evmone/instructions_traits.hpp | 0 .../evmone/instructions_xmacro.hpp | 0 12 files changed, 8 insertions(+), 8 deletions(-) rename lib/{ => assigner}/evmone/baseline.cpp (100%) rename lib/{ => assigner}/evmone/baseline.hpp (100%) rename lib/{ => assigner}/evmone/baseline_instruction_table.cpp (100%) rename lib/{ => assigner}/evmone/baseline_instruction_table.hpp (100%) rename lib/{ => assigner}/evmone/eof.cpp (100%) rename lib/{ => assigner}/evmone/eof.hpp (100%) rename lib/{ => assigner}/evmone/execution_state.hpp (100%) rename lib/{ => assigner}/evmone/instructions.hpp (100%) rename lib/{ => assigner}/evmone/instructions_opcodes.hpp (100%) rename lib/{ => assigner}/evmone/instructions_traits.hpp (100%) rename lib/{ => assigner}/evmone/instructions_xmacro.hpp (100%) diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index e50fb79b..0ec29dc6 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -6,9 +6,9 @@ cmake_policy(SET CMP0063 NEW) option(BUILD_ASSIGNER_TESTS "Build unit tests" FALSE) set(evmone_sources - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline_instruction_table.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/baseline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/baseline_instruction_table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/eof.cpp ) add_library(${PROJECT_NAME} STATIC ${evmone_sources}) @@ -22,7 +22,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC $ $ $ - $) + $) target_link_libraries(${PROJECT_NAME} PUBLIC evmc::evmc intx::intx crypto3::all blueprint ethash::keccak) @@ -41,10 +41,10 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ install(TARGETS ${PROJECT_NAME} EXPORT assignerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) # Install evmone headers -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/execution_state.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/baseline.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/eof.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/../evmone/instructions_opcodes.hpp +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/evmone/execution_state.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/baseline.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/eof.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/evmone/instructions_opcodes.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) if(BUILD_ASSIGNER_TESTS) add_subdirectory(test) diff --git a/lib/evmone/baseline.cpp b/lib/assigner/evmone/baseline.cpp similarity index 100% rename from lib/evmone/baseline.cpp rename to lib/assigner/evmone/baseline.cpp diff --git a/lib/evmone/baseline.hpp b/lib/assigner/evmone/baseline.hpp similarity index 100% rename from lib/evmone/baseline.hpp rename to lib/assigner/evmone/baseline.hpp diff --git a/lib/evmone/baseline_instruction_table.cpp b/lib/assigner/evmone/baseline_instruction_table.cpp similarity index 100% rename from lib/evmone/baseline_instruction_table.cpp rename to lib/assigner/evmone/baseline_instruction_table.cpp diff --git a/lib/evmone/baseline_instruction_table.hpp b/lib/assigner/evmone/baseline_instruction_table.hpp similarity index 100% rename from lib/evmone/baseline_instruction_table.hpp rename to lib/assigner/evmone/baseline_instruction_table.hpp diff --git a/lib/evmone/eof.cpp b/lib/assigner/evmone/eof.cpp similarity index 100% rename from lib/evmone/eof.cpp rename to lib/assigner/evmone/eof.cpp diff --git a/lib/evmone/eof.hpp b/lib/assigner/evmone/eof.hpp similarity index 100% rename from lib/evmone/eof.hpp rename to lib/assigner/evmone/eof.hpp diff --git a/lib/evmone/execution_state.hpp b/lib/assigner/evmone/execution_state.hpp similarity index 100% rename from lib/evmone/execution_state.hpp rename to lib/assigner/evmone/execution_state.hpp diff --git a/lib/evmone/instructions.hpp b/lib/assigner/evmone/instructions.hpp similarity index 100% rename from lib/evmone/instructions.hpp rename to lib/assigner/evmone/instructions.hpp diff --git a/lib/evmone/instructions_opcodes.hpp b/lib/assigner/evmone/instructions_opcodes.hpp similarity index 100% rename from lib/evmone/instructions_opcodes.hpp rename to lib/assigner/evmone/instructions_opcodes.hpp diff --git a/lib/evmone/instructions_traits.hpp b/lib/assigner/evmone/instructions_traits.hpp similarity index 100% rename from lib/evmone/instructions_traits.hpp rename to lib/assigner/evmone/instructions_traits.hpp diff --git a/lib/evmone/instructions_xmacro.hpp b/lib/assigner/evmone/instructions_xmacro.hpp similarity index 100% rename from lib/evmone/instructions_xmacro.hpp rename to lib/assigner/evmone/instructions_xmacro.hpp From 5d25cefa10674c1560e888670dffe8f14f1c26f9 Mon Sep 17 00:00:00 2001 From: akokoshn Date: Tue, 18 Jun 2024 10:59:11 +0300 Subject: [PATCH 6/7] Rename template struct operation to instructions --- lib/assigner/evmone/instructions.hpp | 204 ++++++------ lib/assigner/evmone/instructions_xmacro.hpp | 330 ++++++++++---------- 2 files changed, 267 insertions(+), 267 deletions(-) diff --git a/lib/assigner/evmone/instructions.hpp b/lib/assigner/evmone/instructions.hpp index 12ddede1..3f8c9f5a 100644 --- a/lib/assigner/evmone/instructions.hpp +++ b/lib/assigner/evmone/instructions.hpp @@ -189,9 +189,9 @@ constexpr auto sstore_costs = []() noexcept { namespace instr::core { template -struct operation { +struct instructions { /// Check memory requirements of a reasonable size. - static inline bool check_memory( + static bool check_memory( int64_t& gas_left, Memory& memory, const nil::blueprint::zkevm_word& offset, uint64_t size) noexcept { // TODO: This should be done in intx. @@ -209,7 +209,7 @@ struct operation { } /// Check memory requirements for "copy" instructions. - static inline bool check_memory( + static bool check_memory( int64_t& gas_left, Memory& memory, const nil::blueprint::zkevm_word& offset, const nil::blueprint::zkevm_word& size) noexcept { if (size == 0) // Copy of size 0 is always valid (even if offset is huge). @@ -232,65 +232,65 @@ struct operation { /// - the `stack` pointer points to the EVM stack top element. /// Moreover, these implementations _do not_ inform about new stack height /// after execution. The adjustment must be performed by the caller. - static inline void noop(StackTop /*stack*/) noexcept {} - static inline void pop(StackTop /*stack*/) noexcept {} - static inline void jumpdest(StackTop /*stack*/) noexcept {} + static void noop(StackTop /*stack*/) noexcept {} + static void pop(StackTop /*stack*/) noexcept {} + static void jumpdest(StackTop /*stack*/) noexcept {} - static inline TermResult stop_impl( + static TermResult stop_impl( StackTop /*stack*/, int64_t gas_left, ExecutionState& /*state*/, evmc_status_code Status) noexcept { return {Status, gas_left}; } - static inline TermResult stop(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static TermResult stop(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return stop_impl(stack, gas_left, state, EVMC_SUCCESS); } - static inline TermResult invalid(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static TermResult invalid(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return stop_impl(stack, gas_left, state, EVMC_INVALID_INSTRUCTION); } - static inline void add(StackTop stack) noexcept + static void add(StackTop stack) noexcept { stack.top() = stack.top() + stack.pop(); } - static inline void mul(StackTop stack, ExecutionState& state) noexcept + static void mul(StackTop stack, ExecutionState& state) noexcept { state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 0) = stack[0].to_uint64(); state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 1) = stack[1].to_uint64(); stack.top() = stack.top() * stack.pop(); } - static inline void sub(StackTop stack) noexcept + static void sub(StackTop stack) noexcept { stack[1] = stack[0] - stack[1]; } - static inline void div(StackTop stack) noexcept + static void div(StackTop stack) noexcept { auto& v = stack[1]; v = v != 0 ? stack[0] / v : 0; } - static inline void sdiv(StackTop stack) noexcept + static void sdiv(StackTop stack) noexcept { auto& v = stack[1]; v = stack[0].sdiv(v); } - static inline void mod(StackTop stack) noexcept + static void mod(StackTop stack) noexcept { auto& v = stack[1]; v = v != 0 ? stack[0] % v : 0; } - static inline void smod(StackTop stack) noexcept + static void smod(StackTop stack) noexcept { auto& v = stack[1]; v = stack[0].smod(v); } - static inline void addmod(StackTop stack) noexcept + static void addmod(StackTop stack) noexcept { const auto& x = stack.pop(); const auto& y = stack.pop(); @@ -298,7 +298,7 @@ struct operation { m = x.addmod(y, m); } - static inline void mulmod(StackTop stack) noexcept + static void mulmod(StackTop stack) noexcept { const auto& x = stack[0]; const auto& y = stack[1]; @@ -306,7 +306,7 @@ struct operation { m = x.mulmod(y, m); } - static inline Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& base = stack.pop(); auto& exponent = stack.top(); @@ -321,7 +321,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void signextend(StackTop stack) noexcept + static void signextend(StackTop stack) noexcept { const auto& ext = stack.pop(); auto& x = stack.top(); @@ -354,61 +354,61 @@ struct operation { } } - static inline void lt(StackTop stack) noexcept + static void lt(StackTop stack) noexcept { const auto& x = stack.pop(); stack[0] = x < stack[0]; } - static inline void gt(StackTop stack) noexcept + static void gt(StackTop stack) noexcept { const auto& x = stack.pop(); stack[0] = stack[0] < x; // Arguments are swapped and < is used. } - static inline void slt(StackTop stack) noexcept + static void slt(StackTop stack) noexcept { const auto& x = stack.pop(); stack[0] = x.slt(stack[0]); } - static inline void sgt(StackTop stack) noexcept + static void sgt(StackTop stack) noexcept { const auto& x = stack.pop(); stack[0] = stack[0].slt(x); // Arguments are swapped and SLT is used. } - static inline void eq(StackTop stack) noexcept + static void eq(StackTop stack) noexcept { stack[1] = stack[0] == stack[1]; } - static inline void iszero(StackTop stack) noexcept + static void iszero(StackTop stack) noexcept { stack.top() = stack.top() == 0; } - static inline void and_(StackTop stack) noexcept + static void and_(StackTop stack) noexcept { stack.top() = stack.top() & stack.pop(); } - static inline void or_(StackTop stack) noexcept + static void or_(StackTop stack) noexcept { stack.top() = stack.top() | stack.pop(); } - static inline void xor_(StackTop stack) noexcept + static void xor_(StackTop stack) noexcept { stack.top() = stack.top() ^stack.pop(); } - static inline void not_(StackTop stack) noexcept + static void not_(StackTop stack) noexcept { stack.top() = ~stack.top(); } - static inline void byte(StackTop stack) noexcept + static void byte(StackTop stack) noexcept { const auto& n = stack.pop(); auto& x = stack.top(); @@ -423,17 +423,17 @@ struct operation { x = nil::blueprint::zkevm_word(byte); } - static inline void shl(StackTop stack) noexcept + static void shl(StackTop stack) noexcept { stack.top() = stack.top() << stack.pop(); } - static inline void shr(StackTop stack) noexcept + static void shr(StackTop stack) noexcept { stack.top() = stack.top() >> stack.pop(); } - static inline void sar(StackTop stack) noexcept + static void sar(StackTop stack) noexcept { const auto& y = stack.pop(); auto& x = stack.top(); @@ -445,7 +445,7 @@ struct operation { x = (x >> y) | (sign_mask << mask_shift); } - static inline Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); auto& size = stack.top(); @@ -466,12 +466,12 @@ struct operation { } - static inline void address(StackTop stack, ExecutionState& state) noexcept + static void address(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.msg->recipient)); } - static inline Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = x.to_address(); @@ -486,24 +486,24 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void origin(StackTop stack, ExecutionState& state) noexcept + static void origin(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_origin)); } - static inline void caller(StackTop stack, ExecutionState& state) noexcept + static void caller(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.msg->sender)); } - static inline void callvalue(StackTop stack, ExecutionState& state) noexcept + static void callvalue(StackTop stack, ExecutionState& state) noexcept { auto val = nil::blueprint::zkevm_word(state.msg->value); state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 0) = val.to_uint64(); stack.push(val); } - static inline void calldataload(StackTop stack, ExecutionState& state) noexcept + static void calldataload(StackTop stack, ExecutionState& state) noexcept { auto& index = stack.top(); state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 1) = index.to_uint64(); @@ -524,12 +524,12 @@ struct operation { } } - static inline void calldatasize(StackTop stack, ExecutionState& state) noexcept + static void calldatasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.msg->input_size); } - static inline Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); @@ -556,12 +556,12 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void codesize(StackTop stack, ExecutionState& state) noexcept + static void codesize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.original_code.size()); } - static inline Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { // TODO: Similar to calldatacopy(). @@ -593,17 +593,17 @@ struct operation { } - static inline void gasprice(StackTop stack, ExecutionState& state) noexcept + static void gasprice(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_gas_price)); } - static inline void basefee(StackTop stack, ExecutionState& state) noexcept + static void basefee(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_base_fee)); } - static inline void blobhash(StackTop stack, ExecutionState& state) noexcept + static void blobhash(StackTop stack, ExecutionState& state) noexcept { auto& index = stack.top(); const auto& tx = state.get_tx_context(); @@ -614,12 +614,12 @@ struct operation { 0; } - static inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept + static void blobbasefee(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().blob_base_fee)); } - static inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = x.to_address(); @@ -634,7 +634,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto addr = stack.pop().to_address(); const auto& mem_index = stack.pop(); @@ -668,12 +668,12 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void returndatasize(StackTop stack, ExecutionState& state) noexcept + static void returndatasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.return_data.size()); } - static inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); @@ -701,7 +701,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = x.to_address(); @@ -717,7 +717,7 @@ struct operation { } - static inline void blockhash(StackTop stack, ExecutionState& state) noexcept + static void blockhash(StackTop stack, ExecutionState& state) noexcept { auto& number = stack.top(); @@ -730,46 +730,46 @@ struct operation { number = nil::blueprint::zkevm_word(header); } - static inline void coinbase(StackTop stack, ExecutionState& state) noexcept + static void coinbase(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_coinbase)); } - static inline void timestamp(StackTop stack, ExecutionState& state) noexcept + static void timestamp(StackTop stack, ExecutionState& state) noexcept { // TODO: Add tests for negative timestamp? stack.push(static_cast(state.get_tx_context().block_timestamp)); } - static inline void number(StackTop stack, ExecutionState& state) noexcept + static void number(StackTop stack, ExecutionState& state) noexcept { // TODO: Add tests for negative block number? stack.push(static_cast(state.get_tx_context().block_number)); } - static inline void prevrandao(StackTop stack, ExecutionState& state) noexcept + static void prevrandao(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_prev_randao)); } - static inline void gaslimit(StackTop stack, ExecutionState& state) noexcept + static void gaslimit(StackTop stack, ExecutionState& state) noexcept { stack.push(static_cast(state.get_tx_context().block_gas_limit)); } - static inline void chainid(StackTop stack, ExecutionState& state) noexcept + static void chainid(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().chain_id)); } - static inline void selfbalance(StackTop stack, ExecutionState& state) noexcept + static void selfbalance(StackTop stack, ExecutionState& state) noexcept { // TODO: introduce selfbalance in EVMC? stack.push(nil::blueprint::zkevm_word(state.host.get_balance(state.msg->recipient))); } template - static inline Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& index = stack.top(); @@ -782,7 +782,7 @@ struct operation { } template - static inline Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); auto& value = stack.pop(); @@ -797,7 +797,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); const auto& value = stack.pop(); @@ -810,7 +810,7 @@ struct operation { } /// Internal jump implementation for JUMP/JUMPI instructions. - static inline code_iterator jump_impl(ExecutionState& state, const nil::blueprint::zkevm_word& dst) noexcept + static code_iterator jump_impl(ExecutionState& state, const nil::blueprint::zkevm_word& dst) noexcept { const auto& jumpdest_map = state.analysis.baseline->jumpdest_map; const auto dst_uint64 = dst.to_uint64(); @@ -824,33 +824,33 @@ struct operation { } /// JUMP instruction implementation using baseline::CodeAnalysis. - static inline code_iterator jump(StackTop stack, ExecutionState& state, code_iterator /*pos*/) noexcept + static code_iterator jump(StackTop stack, ExecutionState& state, code_iterator /*pos*/) noexcept { return jump_impl(state, stack.pop()); } /// JUMPI instruction implementation using baseline::CodeAnalysis. - static inline code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto& dst = stack.pop(); const auto& cond = stack.pop(); return cond.to_uint64() > 0 ? jump_impl(state, dst) : pos + 1; } - static inline code_iterator rjump(StackTop /*stack*/, ExecutionState& /*state*/, code_iterator pc) noexcept + static code_iterator rjump(StackTop /*stack*/, ExecutionState& /*state*/, code_iterator pc) noexcept { // Reading next 2 bytes is guaranteed to be safe by deploy-time validation. const auto offset = read_int16_be(&pc[1]); return pc + 3 + offset; // PC_post_rjump + offset } - static inline code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept + static code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept { const auto cond = stack.pop(); return cond.to_uint64() > 0 ? rjump(stack, state, pc) : pc + 3; } - static inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept + static code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept { constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); const auto case_ = stack.pop(); @@ -871,24 +871,24 @@ struct operation { } } - static inline code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); return pos + 1; } - static inline void msize(StackTop stack, ExecutionState& state) noexcept + static void msize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.memory.size()); } - static inline Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept + static Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept { stack.push(gas_left); return {EVMC_SUCCESS, gas_left}; } - static inline void tload(StackTop stack, ExecutionState& state) noexcept + static void tload(StackTop stack, ExecutionState& state) noexcept { auto& x = stack.top(); evmc::bytes32 key = x.to_uint256be(); @@ -897,7 +897,7 @@ struct operation { state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 2) = x.to_uint64(); } - static inline Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { if (state.in_static_mode()) return {EVMC_STATIC_MODE_VIOLATION, 0}; @@ -910,7 +910,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void push0(StackTop stack) noexcept + static void push0(StackTop stack) noexcept { stack.push({}); } @@ -920,7 +920,7 @@ struct operation { /// /// It assumes that at lest 32 bytes of data are available so code padding is required. template - static inline code_iterator push(StackTop stack, ExecutionState& /*state*/, code_iterator pos) noexcept + static code_iterator push(StackTop stack, ExecutionState& /*state*/, code_iterator pos) noexcept { // TODO size of field in bytes constexpr size_t word_size = 8; @@ -951,7 +951,7 @@ struct operation { /// DUP instruction implementation. /// @tparam N The number as in the instruction definition, e.g. DUP3 is dup<3>. template - static inline void dup(StackTop stack) noexcept + static void dup(StackTop stack) noexcept { static_assert(N >= 0 && N <= 16); if constexpr (N == 0) @@ -969,7 +969,7 @@ struct operation { /// SWAP instruction implementation. /// @tparam N The number as in the instruction definition, e.g. SWAP3 is swap<3>. template - static inline void swap(StackTop stack) noexcept + static void swap(StackTop stack) noexcept { static_assert(N >= 0 && N <= 16); @@ -999,7 +999,7 @@ struct operation { a->set_val(t3, 3); } - static inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto n = pos[1] + 1; @@ -1016,7 +1016,7 @@ struct operation { return pos + 2; } - static inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto n = pos[1] + 1; @@ -1034,7 +1034,7 @@ struct operation { return pos + 2; } - static inline Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& dst_u256 = stack.pop(); const auto& src_u256 = stack.pop(); @@ -1056,7 +1056,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline void dataload(StackTop stack, ExecutionState& state) noexcept + static void dataload(StackTop stack, ExecutionState& state) noexcept { auto& index = stack.top(); state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 2) = index.to_uint64(); @@ -1076,12 +1076,12 @@ struct operation { } } - static inline void datasize(StackTop stack, ExecutionState& state) noexcept + static void datasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.data.size()); } - static inline code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto index = read_uint16_be(&pos[1]); @@ -1089,7 +1089,7 @@ struct operation { return pos + 3; } - static inline Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& mem_index = stack.pop(); const auto& data_index = stack.pop(); @@ -1118,7 +1118,7 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state, Opcode Op) noexcept + static Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state, Opcode Op) noexcept { assert(Op == OP_CALL || Op == OP_CALLCODE || Op == OP_DELEGATECALL || Op == OP_STATICCALL); @@ -1234,19 +1234,19 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result call(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static Result call(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return call_impl(stack, gas_left, state, OP_CALL); } - static inline Result callcode(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static Result callcode(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return call_impl(stack, gas_left, state, OP_CALLCODE); } - static inline Result delegatecall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static Result delegatecall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return call_impl(stack, gas_left, state, OP_DELEGATECALL); } - static inline Result staticcall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static Result staticcall(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return call_impl(stack, gas_left, state, OP_STATICCALL); } @@ -1313,15 +1313,15 @@ struct operation { return {EVMC_SUCCESS, gas_left}; } - static inline Result create(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static Result create(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return create_impl(stack, gas_left, state, OP_CREATE); } - static inline constexpr auto create2(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static constexpr auto create2(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return create_impl(stack, gas_left, state, OP_CREATE2); } - static inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto index = read_uint16_be(&pos[1]); const auto& header = state.analysis.baseline->eof_header; @@ -1348,14 +1348,14 @@ struct operation { return code.data() + offset; } - static inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept + static code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept { const auto p = state.call_stack.back(); state.call_stack.pop_back(); return p; } - static inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept + static code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto index = read_uint16_be(&pos[1]); const auto& header = state.analysis.baseline->eof_header; @@ -1374,7 +1374,7 @@ struct operation { return code.data() + offset; } - static inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state, evmc_status_code StatusCode) noexcept + static TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state, evmc_status_code StatusCode) noexcept { const auto& offset = stack[0]; const auto& size = stack[1]; @@ -1387,15 +1387,15 @@ struct operation { state.output_offset = offset.to_uint64(); return {StatusCode, gas_left}; } - static inline TermResult return_(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static TermResult return_(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return return_impl(stack, gas_left, state, EVMC_SUCCESS); } - static inline TermResult revert(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + static TermResult revert(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { return return_impl(stack, gas_left, state, EVMC_REVERT); } - static inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept + static TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { if (state.in_static_mode()) return {EVMC_STATIC_MODE_VIOLATION, gas_left}; @@ -1479,6 +1479,6 @@ struct operation { state.gas_refund += gas_refund; return {EVMC_SUCCESS, gas_left}; } -}; // class operation +}; // struct instructions } // namespace instr::core } // namespace evmone diff --git a/lib/assigner/evmone/instructions_xmacro.hpp b/lib/assigner/evmone/instructions_xmacro.hpp index b41fbcd8..f97aaeda 100644 --- a/lib/assigner/evmone/instructions_xmacro.hpp +++ b/lib/assigner/evmone/instructions_xmacro.hpp @@ -32,41 +32,41 @@ /// /// See for more about X Macros: https://en.wikipedia.org/wiki/X_Macro. #define MAP_OPCODES \ - ON_OPCODE_IDENTIFIER(OP_STOP, operation::stop) \ - ON_OPCODE_IDENTIFIER(OP_ADD, operation::add) \ - ON_OPCODE_IDENTIFIER(OP_MUL, operation::mul) \ - ON_OPCODE_IDENTIFIER(OP_SUB, operation::sub) \ - ON_OPCODE_IDENTIFIER(OP_DIV, operation::div) \ - ON_OPCODE_IDENTIFIER(OP_SDIV, operation::sdiv) \ - ON_OPCODE_IDENTIFIER(OP_MOD, operation::mod) \ - ON_OPCODE_IDENTIFIER(OP_SMOD, operation::smod) \ - ON_OPCODE_IDENTIFIER(OP_ADDMOD, operation::addmod) \ - ON_OPCODE_IDENTIFIER(OP_MULMOD, operation::mulmod) \ - ON_OPCODE_IDENTIFIER(OP_EXP, operation::exp) \ - ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, operation::signextend) \ + ON_OPCODE_IDENTIFIER(OP_STOP, instructions::stop) \ + ON_OPCODE_IDENTIFIER(OP_ADD, instructions::add) \ + ON_OPCODE_IDENTIFIER(OP_MUL, instructions::mul) \ + ON_OPCODE_IDENTIFIER(OP_SUB, instructions::sub) \ + ON_OPCODE_IDENTIFIER(OP_DIV, instructions::div) \ + ON_OPCODE_IDENTIFIER(OP_SDIV, instructions::sdiv) \ + ON_OPCODE_IDENTIFIER(OP_MOD, instructions::mod) \ + ON_OPCODE_IDENTIFIER(OP_SMOD, instructions::smod) \ + ON_OPCODE_IDENTIFIER(OP_ADDMOD, instructions::addmod) \ + ON_OPCODE_IDENTIFIER(OP_MULMOD, instructions::mulmod) \ + ON_OPCODE_IDENTIFIER(OP_EXP, instructions::exp) \ + ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, instructions::signextend) \ ON_OPCODE_UNDEFINED(0x0c) \ ON_OPCODE_UNDEFINED(0x0d) \ ON_OPCODE_UNDEFINED(0x0e) \ ON_OPCODE_UNDEFINED(0x0f) \ \ - ON_OPCODE_IDENTIFIER(OP_LT, operation::lt) \ - ON_OPCODE_IDENTIFIER(OP_GT, operation::gt) \ - ON_OPCODE_IDENTIFIER(OP_SLT, operation::slt) \ - ON_OPCODE_IDENTIFIER(OP_SGT, operation::sgt) \ - ON_OPCODE_IDENTIFIER(OP_EQ, operation::eq) \ - ON_OPCODE_IDENTIFIER(OP_ISZERO, operation::iszero) \ - ON_OPCODE_IDENTIFIER(OP_AND, operation::and_) \ - ON_OPCODE_IDENTIFIER(OP_OR, operation::or_) \ - ON_OPCODE_IDENTIFIER(OP_XOR, operation::xor_) \ - ON_OPCODE_IDENTIFIER(OP_NOT, operation::not_) \ - ON_OPCODE_IDENTIFIER(OP_BYTE, operation::byte) \ - ON_OPCODE_IDENTIFIER(OP_SHL, operation::shl) \ - ON_OPCODE_IDENTIFIER(OP_SHR, operation::shr) \ - ON_OPCODE_IDENTIFIER(OP_SAR, operation::sar) \ + ON_OPCODE_IDENTIFIER(OP_LT, instructions::lt) \ + ON_OPCODE_IDENTIFIER(OP_GT, instructions::gt) \ + ON_OPCODE_IDENTIFIER(OP_SLT, instructions::slt) \ + ON_OPCODE_IDENTIFIER(OP_SGT, instructions::sgt) \ + ON_OPCODE_IDENTIFIER(OP_EQ, instructions::eq) \ + ON_OPCODE_IDENTIFIER(OP_ISZERO, instructions::iszero) \ + ON_OPCODE_IDENTIFIER(OP_AND, instructions::and_) \ + ON_OPCODE_IDENTIFIER(OP_OR, instructions::or_) \ + ON_OPCODE_IDENTIFIER(OP_XOR, instructions::xor_) \ + ON_OPCODE_IDENTIFIER(OP_NOT, instructions::not_) \ + ON_OPCODE_IDENTIFIER(OP_BYTE, instructions::byte) \ + ON_OPCODE_IDENTIFIER(OP_SHL, instructions::shl) \ + ON_OPCODE_IDENTIFIER(OP_SHR, instructions::shr) \ + ON_OPCODE_IDENTIFIER(OP_SAR, instructions::sar) \ ON_OPCODE_UNDEFINED(0x1e) \ ON_OPCODE_UNDEFINED(0x1f) \ \ - ON_OPCODE_IDENTIFIER(OP_KECCAK256, operation::keccak256) \ + ON_OPCODE_IDENTIFIER(OP_KECCAK256, instructions::keccak256) \ ON_OPCODE_UNDEFINED(0x21) \ ON_OPCODE_UNDEFINED(0x22) \ ON_OPCODE_UNDEFINED(0x23) \ @@ -83,134 +83,134 @@ ON_OPCODE_UNDEFINED(0x2e) \ ON_OPCODE_UNDEFINED(0x2f) \ \ - ON_OPCODE_IDENTIFIER(OP_ADDRESS, operation::address) \ - ON_OPCODE_IDENTIFIER(OP_BALANCE, operation::balance) \ - ON_OPCODE_IDENTIFIER(OP_ORIGIN, operation::origin) \ - ON_OPCODE_IDENTIFIER(OP_CALLER, operation::caller) \ - ON_OPCODE_IDENTIFIER(OP_CALLVALUE, operation::callvalue) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, operation::calldataload) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, operation::calldatasize) \ - ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, operation::calldatacopy) \ - ON_OPCODE_IDENTIFIER(OP_CODESIZE, operation::codesize) \ - ON_OPCODE_IDENTIFIER(OP_CODECOPY, operation::codecopy) \ - ON_OPCODE_IDENTIFIER(OP_GASPRICE, operation::gasprice) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, operation::extcodesize) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, operation::extcodecopy) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, operation::returndatasize) \ - ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, operation::returndatacopy) \ - ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, operation::extcodehash) \ + ON_OPCODE_IDENTIFIER(OP_ADDRESS, instructions::address) \ + ON_OPCODE_IDENTIFIER(OP_BALANCE, instructions::balance) \ + ON_OPCODE_IDENTIFIER(OP_ORIGIN, instructions::origin) \ + ON_OPCODE_IDENTIFIER(OP_CALLER, instructions::caller) \ + ON_OPCODE_IDENTIFIER(OP_CALLVALUE, instructions::callvalue) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATALOAD, instructions::calldataload) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATASIZE, instructions::calldatasize) \ + ON_OPCODE_IDENTIFIER(OP_CALLDATACOPY, instructions::calldatacopy) \ + ON_OPCODE_IDENTIFIER(OP_CODESIZE, instructions::codesize) \ + ON_OPCODE_IDENTIFIER(OP_CODECOPY, instructions::codecopy) \ + ON_OPCODE_IDENTIFIER(OP_GASPRICE, instructions::gasprice) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODESIZE, instructions::extcodesize) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODECOPY, instructions::extcodecopy) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATASIZE, instructions::returndatasize) \ + ON_OPCODE_IDENTIFIER(OP_RETURNDATACOPY, instructions::returndatacopy) \ + ON_OPCODE_IDENTIFIER(OP_EXTCODEHASH, instructions::extcodehash) \ \ - ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, operation::blockhash) \ - ON_OPCODE_IDENTIFIER(OP_COINBASE, operation::coinbase) \ - ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, operation::timestamp) \ - ON_OPCODE_IDENTIFIER(OP_NUMBER, operation::number) \ - ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, operation::prevrandao) \ - ON_OPCODE_IDENTIFIER(OP_GASLIMIT, operation::gaslimit) \ - ON_OPCODE_IDENTIFIER(OP_CHAINID, operation::chainid) \ - ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, operation::selfbalance) \ - ON_OPCODE_IDENTIFIER(OP_BASEFEE, operation::basefee) \ - ON_OPCODE_IDENTIFIER(OP_BLOBHASH, operation::blobhash) \ - ON_OPCODE_IDENTIFIER(OP_BLOBBASEFEE, operation::blobbasefee) \ + ON_OPCODE_IDENTIFIER(OP_BLOCKHASH, instructions::blockhash) \ + ON_OPCODE_IDENTIFIER(OP_COINBASE, instructions::coinbase) \ + ON_OPCODE_IDENTIFIER(OP_TIMESTAMP, instructions::timestamp) \ + ON_OPCODE_IDENTIFIER(OP_NUMBER, instructions::number) \ + ON_OPCODE_IDENTIFIER(OP_PREVRANDAO, instructions::prevrandao) \ + ON_OPCODE_IDENTIFIER(OP_GASLIMIT, instructions::gaslimit) \ + ON_OPCODE_IDENTIFIER(OP_CHAINID, instructions::chainid) \ + ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, instructions::selfbalance) \ + ON_OPCODE_IDENTIFIER(OP_BASEFEE, instructions::basefee) \ + ON_OPCODE_IDENTIFIER(OP_BLOBHASH, instructions::blobhash) \ + ON_OPCODE_IDENTIFIER(OP_BLOBBASEFEE, instructions::blobbasefee) \ ON_OPCODE_UNDEFINED(0x4b) \ ON_OPCODE_UNDEFINED(0x4c) \ ON_OPCODE_UNDEFINED(0x4d) \ ON_OPCODE_UNDEFINED(0x4e) \ ON_OPCODE_UNDEFINED(0x4f) \ \ - ON_OPCODE_IDENTIFIER(OP_POP, operation::pop) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD, operation::template mload::value_type>) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD8, operation::template mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD16, operation::template mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD32, operation::template mload) \ - ON_OPCODE_IDENTIFIER(OP_MLOAD64, operation::template mload) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE, operation::template mstore::value_type>) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE8, operation::template mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE16, operation::template mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE32, operation::template mstore) \ - ON_OPCODE_IDENTIFIER(OP_MSTORE64, operation::template mstore) \ - ON_OPCODE_IDENTIFIER(OP_SLOAD, operation::sload) \ - ON_OPCODE_IDENTIFIER(OP_SSTORE, operation::sstore) \ - ON_OPCODE_IDENTIFIER(OP_JUMP, operation::jump) \ - ON_OPCODE_IDENTIFIER(OP_JUMPI, operation::jumpi) \ - ON_OPCODE_IDENTIFIER(OP_PC, operation::pc) \ - ON_OPCODE_IDENTIFIER(OP_MSIZE, operation::msize) \ - ON_OPCODE_IDENTIFIER(OP_GAS, operation::gas) \ - ON_OPCODE_IDENTIFIER(OP_JUMPDEST, operation::jumpdest) \ - ON_OPCODE_IDENTIFIER(OP_TLOAD, operation::tload) \ - ON_OPCODE_IDENTIFIER(OP_TSTORE, operation::tstore) \ - ON_OPCODE_IDENTIFIER(OP_MCOPY, operation::mcopy) \ - ON_OPCODE_IDENTIFIER(OP_PUSH0, operation::push0) \ + ON_OPCODE_IDENTIFIER(OP_POP, instructions::pop) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD, instructions::template mload::value_type>) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD8, instructions::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD16, instructions::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD32, instructions::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MLOAD64, instructions::template mload) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE, instructions::template mstore::value_type>) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE8, instructions::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE16, instructions::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE32, instructions::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_MSTORE64, instructions::template mstore) \ + ON_OPCODE_IDENTIFIER(OP_SLOAD, instructions::sload) \ + ON_OPCODE_IDENTIFIER(OP_SSTORE, instructions::sstore) \ + ON_OPCODE_IDENTIFIER(OP_JUMP, instructions::jump) \ + ON_OPCODE_IDENTIFIER(OP_JUMPI, instructions::jumpi) \ + ON_OPCODE_IDENTIFIER(OP_PC, instructions::pc) \ + ON_OPCODE_IDENTIFIER(OP_MSIZE, instructions::msize) \ + ON_OPCODE_IDENTIFIER(OP_GAS, instructions::gas) \ + ON_OPCODE_IDENTIFIER(OP_JUMPDEST, instructions::jumpdest) \ + ON_OPCODE_IDENTIFIER(OP_TLOAD, instructions::tload) \ + ON_OPCODE_IDENTIFIER(OP_TSTORE, instructions::tstore) \ + ON_OPCODE_IDENTIFIER(OP_MCOPY, instructions::mcopy) \ + ON_OPCODE_IDENTIFIER(OP_PUSH0, instructions::push0) \ \ - ON_OPCODE_IDENTIFIER(OP_PUSH1, operation::template push<1>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH2, operation::template push<2>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH3, operation::template push<3>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH4, operation::template push<4>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH5, operation::template push<5>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH6, operation::template push<6>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH7, operation::template push<7>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH8, operation::template push<8>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH9, operation::template push<9>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH10, operation::template push<10>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH11, operation::template push<11>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH12, operation::template push<12>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH13, operation::template push<13>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH14, operation::template push<14>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH15, operation::template push<15>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH1, instructions::template push<1>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH2, instructions::template push<2>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH3, instructions::template push<3>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH4, instructions::template push<4>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH5, instructions::template push<5>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH6, instructions::template push<6>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH7, instructions::template push<7>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH8, instructions::template push<8>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH9, instructions::template push<9>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH10, instructions::template push<10>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH11, instructions::template push<11>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH12, instructions::template push<12>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH13, instructions::template push<13>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH14, instructions::template push<14>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH15, instructions::template push<15>) \ \ - ON_OPCODE_IDENTIFIER(OP_PUSH16, operation::template push<16>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH17, operation::template push<17>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH18, operation::template push<18>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH19, operation::template push<19>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH20, operation::template push<20>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH21, operation::template push<21>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH22, operation::template push<22>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH23, operation::template push<23>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH24, operation::template push<24>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH25, operation::template push<25>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH26, operation::template push<26>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH27, operation::template push<27>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH28, operation::template push<28>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH29, operation::template push<29>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH30, operation::template push<30>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH31, operation::template push<31>) \ - ON_OPCODE_IDENTIFIER(OP_PUSH32, operation::template push<32>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH16, instructions::template push<16>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH17, instructions::template push<17>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH18, instructions::template push<18>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH19, instructions::template push<19>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH20, instructions::template push<20>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH21, instructions::template push<21>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH22, instructions::template push<22>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH23, instructions::template push<23>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH24, instructions::template push<24>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH25, instructions::template push<25>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH26, instructions::template push<26>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH27, instructions::template push<27>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH28, instructions::template push<28>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH29, instructions::template push<29>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH30, instructions::template push<30>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH31, instructions::template push<31>) \ + ON_OPCODE_IDENTIFIER(OP_PUSH32, instructions::template push<32>) \ \ - ON_OPCODE_IDENTIFIER(OP_DUP1, operation::template dup<1>) \ - ON_OPCODE_IDENTIFIER(OP_DUP2, operation::template dup<2>) \ - ON_OPCODE_IDENTIFIER(OP_DUP3, operation::template dup<3>) \ - ON_OPCODE_IDENTIFIER(OP_DUP4, operation::template dup<4>) \ - ON_OPCODE_IDENTIFIER(OP_DUP5, operation::template dup<5>) \ - ON_OPCODE_IDENTIFIER(OP_DUP6, operation::template dup<6>) \ - ON_OPCODE_IDENTIFIER(OP_DUP7, operation::template dup<7>) \ - ON_OPCODE_IDENTIFIER(OP_DUP8, operation::template dup<8>) \ - ON_OPCODE_IDENTIFIER(OP_DUP9, operation::template dup<9>) \ - ON_OPCODE_IDENTIFIER(OP_DUP10, operation::template dup<10>) \ - ON_OPCODE_IDENTIFIER(OP_DUP11, operation::template dup<11>) \ - ON_OPCODE_IDENTIFIER(OP_DUP12, operation::template dup<12>) \ - ON_OPCODE_IDENTIFIER(OP_DUP13, operation::template dup<13>) \ - ON_OPCODE_IDENTIFIER(OP_DUP14, operation::template dup<14>) \ - ON_OPCODE_IDENTIFIER(OP_DUP15, operation::template dup<15>) \ - ON_OPCODE_IDENTIFIER(OP_DUP16, operation::template dup<16>) \ + ON_OPCODE_IDENTIFIER(OP_DUP1, instructions::template dup<1>) \ + ON_OPCODE_IDENTIFIER(OP_DUP2, instructions::template dup<2>) \ + ON_OPCODE_IDENTIFIER(OP_DUP3, instructions::template dup<3>) \ + ON_OPCODE_IDENTIFIER(OP_DUP4, instructions::template dup<4>) \ + ON_OPCODE_IDENTIFIER(OP_DUP5, instructions::template dup<5>) \ + ON_OPCODE_IDENTIFIER(OP_DUP6, instructions::template dup<6>) \ + ON_OPCODE_IDENTIFIER(OP_DUP7, instructions::template dup<7>) \ + ON_OPCODE_IDENTIFIER(OP_DUP8, instructions::template dup<8>) \ + ON_OPCODE_IDENTIFIER(OP_DUP9, instructions::template dup<9>) \ + ON_OPCODE_IDENTIFIER(OP_DUP10, instructions::template dup<10>) \ + ON_OPCODE_IDENTIFIER(OP_DUP11, instructions::template dup<11>) \ + ON_OPCODE_IDENTIFIER(OP_DUP12, instructions::template dup<12>) \ + ON_OPCODE_IDENTIFIER(OP_DUP13, instructions::template dup<13>) \ + ON_OPCODE_IDENTIFIER(OP_DUP14, instructions::template dup<14>) \ + ON_OPCODE_IDENTIFIER(OP_DUP15, instructions::template dup<15>) \ + ON_OPCODE_IDENTIFIER(OP_DUP16, instructions::template dup<16>) \ \ - ON_OPCODE_IDENTIFIER(OP_SWAP1, operation::template swap<1>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP2, operation::template swap<2>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP3, operation::template swap<3>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP4, operation::template swap<4>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP5, operation::template swap<5>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP6, operation::template swap<6>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP7, operation::template swap<7>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP8, operation::template swap<8>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP9, operation::template swap<9>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP10, operation::template swap<10>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP11, operation::template swap<11>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP12, operation::template swap<12>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP13, operation::template swap<13>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP14, operation::template swap<14>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP15, operation::template swap<15>) \ - ON_OPCODE_IDENTIFIER(OP_SWAP16, operation::template swap<16>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP1, instructions::template swap<1>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP2, instructions::template swap<2>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP3, instructions::template swap<3>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP4, instructions::template swap<4>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP5, instructions::template swap<5>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP6, instructions::template swap<6>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP7, instructions::template swap<7>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP8, instructions::template swap<8>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP9, instructions::template swap<9>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP10, instructions::template swap<10>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP11, instructions::template swap<11>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP12, instructions::template swap<12>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP13, instructions::template swap<13>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP14, instructions::template swap<14>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP15, instructions::template swap<15>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP16, instructions::template swap<16>) \ \ - ON_OPCODE_IDENTIFIER(OP_SWAP, operation::template swap<0>) \ - ON_OPCODE_IDENTIFIER(OP_DUP, operation::template dup<0>) \ + ON_OPCODE_IDENTIFIER(OP_SWAP, instructions::template swap<0>) \ + ON_OPCODE_IDENTIFIER(OP_DUP, instructions::template dup<0>) \ ON_OPCODE_UNDEFINED(0xa8) \ ON_OPCODE_UNDEFINED(0xa9) \ ON_OPCODE_UNDEFINED(0xaa) \ @@ -264,36 +264,36 @@ ON_OPCODE_UNDEFINED(0xde) \ ON_OPCODE_UNDEFINED(0xdf) \ \ - ON_OPCODE_IDENTIFIER(OP_RJUMP, operation::rjump) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPI, operation::rjumpi) \ - ON_OPCODE_IDENTIFIER(OP_RJUMPV, operation::rjumpv) \ - ON_OPCODE_IDENTIFIER(OP_CALLF, operation::callf) \ - ON_OPCODE_IDENTIFIER(OP_RETF, operation::retf) \ - ON_OPCODE_IDENTIFIER(OP_JUMPF, operation::jumpf) \ - ON_OPCODE_IDENTIFIER(OP_DUPN, operation::dupn) \ - ON_OPCODE_IDENTIFIER(OP_SWAPN, operation::swapn) \ - ON_OPCODE_IDENTIFIER(OP_DATALOAD, operation::dataload) \ - ON_OPCODE_IDENTIFIER(OP_DATALOADN, operation::dataloadn) \ - ON_OPCODE_IDENTIFIER(OP_DATASIZE, operation::datasize) \ - ON_OPCODE_IDENTIFIER(OP_DATACOPY, operation::datacopy) \ + ON_OPCODE_IDENTIFIER(OP_RJUMP, instructions::rjump) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPI, instructions::rjumpi) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPV, instructions::rjumpv) \ + ON_OPCODE_IDENTIFIER(OP_CALLF, instructions::callf) \ + ON_OPCODE_IDENTIFIER(OP_RETF, instructions::retf) \ + ON_OPCODE_IDENTIFIER(OP_JUMPF, instructions::jumpf) \ + ON_OPCODE_IDENTIFIER(OP_DUPN, instructions::dupn) \ + ON_OPCODE_IDENTIFIER(OP_SWAPN, instructions::swapn) \ + ON_OPCODE_IDENTIFIER(OP_DATALOAD, instructions::dataload) \ + ON_OPCODE_IDENTIFIER(OP_DATALOADN, instructions::dataloadn) \ + ON_OPCODE_IDENTIFIER(OP_DATASIZE, instructions::datasize) \ + ON_OPCODE_IDENTIFIER(OP_DATACOPY, instructions::datacopy) \ ON_OPCODE_UNDEFINED(0xec) \ ON_OPCODE_UNDEFINED(0xed) \ ON_OPCODE_UNDEFINED(0xee) \ ON_OPCODE_UNDEFINED(0xef) \ \ - ON_OPCODE_IDENTIFIER(OP_CREATE, operation::create) \ - ON_OPCODE_IDENTIFIER(OP_CALL, operation::call) \ - ON_OPCODE_IDENTIFIER(OP_CALLCODE, operation::callcode) \ - ON_OPCODE_IDENTIFIER(OP_RETURN, operation::return_) \ - ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, operation::delegatecall) \ - ON_OPCODE_IDENTIFIER(OP_CREATE2, operation::create2) \ + ON_OPCODE_IDENTIFIER(OP_CREATE, instructions::create) \ + ON_OPCODE_IDENTIFIER(OP_CALL, instructions::call) \ + ON_OPCODE_IDENTIFIER(OP_CALLCODE, instructions::callcode) \ + ON_OPCODE_IDENTIFIER(OP_RETURN, instructions::return_) \ + ON_OPCODE_IDENTIFIER(OP_DELEGATECALL, instructions::delegatecall) \ + ON_OPCODE_IDENTIFIER(OP_CREATE2, instructions::create2) \ ON_OPCODE_UNDEFINED(0xf6) \ ON_OPCODE_UNDEFINED(0xf7) \ ON_OPCODE_UNDEFINED(0xf8) \ ON_OPCODE_UNDEFINED(0xf9) \ - ON_OPCODE_IDENTIFIER(OP_STATICCALL, operation::staticcall) \ + ON_OPCODE_IDENTIFIER(OP_STATICCALL, instructions::staticcall) \ ON_OPCODE_UNDEFINED(0xfb) \ ON_OPCODE_UNDEFINED(0xfc) \ - ON_OPCODE_IDENTIFIER(OP_REVERT, operation::revert) \ - ON_OPCODE_IDENTIFIER(OP_INVALID, operation::invalid) \ - ON_OPCODE_IDENTIFIER(OP_SELFDESTRUCT, operation::selfdestruct) + ON_OPCODE_IDENTIFIER(OP_REVERT, instructions::revert) \ + ON_OPCODE_IDENTIFIER(OP_INVALID, instructions::invalid) \ + ON_OPCODE_IDENTIFIER(OP_SELFDESTRUCT, instructions::selfdestruct) From c22851f4978653f9a419225264a0ffb60ac59845 Mon Sep 17 00:00:00 2001 From: akokoshn Date: Tue, 18 Jun 2024 15:16:15 +0300 Subject: [PATCH 7/7] Fix cmake --- CMakeLists.txt | 5 ----- cmake/Config.cmake.in | 1 - lib/assigner/CMakeLists.txt | 1 - lib/assigner/evmone/execution_state.hpp | 2 +- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f396a6..b7055f83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,15 +110,10 @@ endif() include(CTest) -set(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include) - add_subdirectory(lib) # INSTALL -install(DIRECTORY ${include_dir}/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - set(CONFIG_PATH ${CMAKE_INSTALL_LIBDIR}/cmake/evm-assigner) set(TARGET_NAMESPACE evm-assigner::) install(EXPORT assignerTargets NAMESPACE ${TARGET_NAMESPACE} DESTINATION ${CONFIG_PATH}) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index c670f9cb..955b28a3 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -7,5 +7,4 @@ find_dependency(evmc REQUIRED) find_dependency(intx REQUIRED) find_dependency(ethash REQUIRED) -include("${CMAKE_CURRENT_LIST_DIR}/evmoneTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/assignerTargets.cmake") diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index 0ec29dc6..e8258e85 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -20,7 +20,6 @@ find_package(blueprint_crypto3 REQUIRED) target_include_directories(${PROJECT_NAME} PUBLIC $ - $ $ $) diff --git a/lib/assigner/evmone/execution_state.hpp b/lib/assigner/evmone/execution_state.hpp index b125f3c7..661a5c58 100644 --- a/lib/assigner/evmone/execution_state.hpp +++ b/lib/assigner/evmone/execution_state.hpp @@ -199,7 +199,7 @@ class ExecutionState const evmc_tx_context& get_tx_context() noexcept { - if (bool(m_tx.block_timestamp == 0)) + if (m_tx.block_timestamp == 0) m_tx = host.get_tx_context(); return m_tx; }