diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c55742f15 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "evmc"] + path = evmc + url = https://github.com/ethereum/evmc diff --git a/CMakeLists.txt b/CMakeLists.txt index ccd4b1c8b..be08120cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,4 +13,7 @@ project(hera) include(ProjectBinaryen) +option(EVMC_BUILD_EXAMPLES "Build EVMC examples" OFF) +add_subdirectory(evmc) + add_subdirectory(src) diff --git a/README.md b/README.md index 10e533fa4..f4d1cb978 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Hera -Hera is an [eWASM](https://github.com/ewasm/design) virtual machine implemented in C++ conforming to the [Ethereum VM API](https://github.com/ethereum/EIPs/issues/56). +Hera is an [ewasm](https://github.com/ewasm/design) virtual machine implemented in C++ conforming to [EVMC](https://github.com/ethereum/evmc/). It can be used with [cpp-ethereum](https://github.com/ethereum/cpp-ethereum) and perhaps in the future with other implementations through appropriate bindings. -Currently it uses [Binaryen](https://github.com/webassembly/binaryen)'s interpreter for running WebAssembly bytecode and it should be improved to support the [WAVM](https://github.com/AndrewScheidecker/WAVM) as a backend. +Currently it uses [Binaryen](https://github.com/webassembly/binaryen)'s interpreter for running WebAssembly bytecode and it should be improved to support [wabt](https://github.com/webassembly/wabt) and [wavm](https://github.com/AndrewScheidecker/WAVM) as backends. ## Build options diff --git a/circle.yml b/circle.yml index f11df076a..d2a7649c8 100644 --- a/circle.yml +++ b/circle.yml @@ -45,7 +45,7 @@ defaults: save-eth-cache: &save-eth-cache save_cache: - key: ð-cache-key cpp-prebuilt-cache-{{arch}}-{{checksum "compiler.version"}}-ee0c6776c01b09045a379220c7e490000dae9377 + key: ð-cache-key cpp-prebuilt-cache-{{arch}}-{{checksum "compiler.version"}}-a17f30a41aa2af506b6b453f052c53bd323a36e0 paths: - ~/build - ~/.hunter @@ -62,7 +62,7 @@ defaults: cd .. git clone https://github.com/ethereum/cpp-ethereum --branch develop --single-branch cd cpp-ethereum - git reset --hard ee0c6776c01b09045a379220c7e490000dae9377 + git reset --hard a17f30a41aa2af506b6b453f052c53bd323a36e0 git submodule update --init link-hera: &link-hera @@ -134,6 +134,7 @@ jobs: - image: ethereum/cpp-build-env steps: - checkout + - *update-submodules - *environment-info - *checkout-cpp-eth - *restore-eth-cache diff --git a/evm.h b/evm.h deleted file mode 100644 index 270d3a9d5..000000000 --- a/evm.h +++ /dev/null @@ -1,530 +0,0 @@ -/// EVM-C -- C interface to Ethereum Virtual Machine -/// -/// ## High level design rules -/// -/// 1. Pass function arguments and results by value. -/// This rule comes from modern C++ and tries to avoid costly alias analysis -/// needed for optimization. As the result we have a lots of complex structs -/// and unions. And variable sized arrays of bytes cannot be passed by copy. -/// 2. The EVM operates on integers so it prefers values to be host-endian. -/// On the other hand, LLVM can generate good code for byte swaping. -/// The interface also tries to match host application "natural" endianess. -/// I would like to know what endianess you use and where. -/// -/// ## Terms -/// -/// 1. EVM -- an Ethereum Virtual Machine instance/implementation. -/// 2. Host -- an entity controlling the EVM. The Host requests code execution -/// and responses to EVM queries by callback functions. -/// -/// @defgroup EVMC EVM-C -/// @{ -#ifndef EVM_H -#define EVM_H - -#include // Definition of int64_t, uint64_t. -#include // Definition of size_t. - -#if __cplusplus -extern "C" { -#endif - -// BEGIN Python CFFI declarations - -enum { - /// The EVM-C ABI version number of the interface declared in this file. - EVM_ABI_VERSION = 0 -}; - -/// Big-endian 256-bit integer. -/// -/// 32 bytes of data representing big-endian 256-bit integer. I.e. bytes[0] is -/// the most significant byte, bytes[31] is the least significant byte. -/// This type is used to transfer to/from the VM values interpreted by the user -/// as both 256-bit integers and 256-bit hashes. -struct evm_uint256be { - /// The 32 bytes of the big-endian integer or hash. - uint8_t bytes[32]; -}; - -/// Big-endian 160-bit hash suitable for keeping an Ethereum address. -struct evm_address { - /// The 20 bytes of the hash. - uint8_t bytes[20]; -}; - -/// The kind of call-like instruction. -enum evm_call_kind { - EVM_CALL = 0, ///< Request CALL. - EVM_DELEGATECALL = 1, ///< Request DELEGATECALL. The value param ignored. - EVM_CALLCODE = 2, ///< Request CALLCODE. - EVM_CREATE = 3, ///< Request CREATE. Semantic of some params changes. -}; - -/// The flags for ::evm_message. -enum evm_flags { - EVM_STATIC = 1 ///< Static call mode. -}; - -/// The message describing an EVM call, -/// including a zero-depth calls from a transaction origin. -struct evm_message { - struct evm_address destination; ///< The destination of the message. - struct evm_address sender; ///< The sender of the message. - - /// The amount of Ether transferred with the message. - struct evm_uint256be value; - - /// The message input data. - /// - /// This MAY be NULL. - const uint8_t* input_data; - - /// The size of the message input data. - /// - /// If input_data is NULL this MUST be 0. - size_t input_size; - - /// The optional hash of the code of the destination account. - /// The null hash MUST be used when not specified. - struct evm_uint256be code_hash; - - int64_t gas; ///< The amount of gas for message execution. - int32_t depth; ///< The call depth. - - /// The kind of the call. For zero-depth calls ::EVM_CALL SHOULD be used. - enum evm_call_kind kind; - - /// Additional flags modifying the call execution behavior. - /// In the current version the only valid values are ::EVM_STATIC or 0. - uint32_t flags; -}; - - -/// The transaction and block data for execution. -struct evm_tx_context { - struct evm_uint256be tx_gas_price; ///< The transaction gas price. - struct evm_address tx_origin; ///< The transaction origin account. - struct evm_address block_coinbase; ///< The miner of the block. - int64_t block_number; ///< The block number. - int64_t block_timestamp; ///< The block timestamp. - int64_t block_gas_limit; ///< The block gas limit. - struct evm_uint256be block_difficulty; ///< The block difficulty. -}; - -struct evm_context; - -/// Get transaction context callback function. -/// -/// This callback function is used by an EVM to retrieve the transaction and -/// block context. -/// -/// @param[out] result The returned transaction context. -/// @see ::evm_tx_context. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -typedef void (*evm_get_tx_context_fn)(struct evm_tx_context* result, - struct evm_context* context); - -/// Get block hash callback function.. -/// -/// This callback function is used by an EVM to query the block hash of -/// a given block. -/// -/// @param[out] result The returned block hash value. -/// @param context The pointer to the Host execution context. -/// @param number The block number. Must be a value between -// (and including) 0 and 255. -typedef void (*evm_get_block_hash_fn)(struct evm_uint256be* result, - struct evm_context* context, - int64_t number); - -/// The execution status code. -enum evm_status_code { - EVM_SUCCESS = 0, ///< Execution finished with success. - EVM_FAILURE = 1, ///< Generic execution failure. - EVM_OUT_OF_GAS = 2, - EVM_BAD_INSTRUCTION = 3, - EVM_BAD_JUMP_DESTINATION = 4, - EVM_STACK_OVERFLOW = 5, - EVM_STACK_UNDERFLOW = 6, - EVM_REVERT = 7, ///< Execution terminated with REVERT opcode. - /// Tried to execute an operation which is restricted in static mode. - EVM_STATIC_MODE_ERROR = 8, - - /// The EVM rejected the execution of the given code or message. - /// - /// This error SHOULD be used to signal that the EVM is not able to or - /// willing to execute the given code type or message. - /// If an EVM returns the ::EVM_REJECTED status code, - /// the Client MAY try to execute it in other EVM implementation. - /// For example, the Client tries running a code in the EVM 1.5. If the - /// code is not supported there, the execution falls back to the EVM 1.0. - EVM_REJECTED = -1, - - /// EVM implementation internal error. - /// - /// @todo We should rethink reporting internal errors. One of the options - /// it to allow using any negative value to represent internal errors. - EVM_INTERNAL_ERROR = -2, -}; - -struct evm_result; ///< Forward declaration. - -/// Releases resources assigned to an execution result. -/// -/// This function releases memory (and other resources, if any) assigned to the -/// specified execution result making the result object invalid. -/// -/// @param result The execution result which resource are to be released. The -/// result itself it not modified by this function, but becomes -/// invalid and user should discard it as well. -typedef void (*evm_release_result_fn)(const struct evm_result* result); - -/// The EVM code execution result. -struct evm_result { - /// The execution status code. - enum evm_status_code status_code; - - /// The amount of gas left after the execution. - /// - /// If evm_result::code is not ::EVM_SUCCESS nor ::EVM_REVERT - /// the value MUST be 0. - int64_t gas_left; - - /// The reference to output data. - /// - /// The output contains data coming from RETURN opcode (iff evm_result::code - /// field is ::EVM_SUCCESS) or from REVERT opcode. - /// - /// The memory containing the output data is owned by EVM and has to be - /// freed with evm_result::release(). - /// - /// This MAY be NULL. - const uint8_t* output_data; - - /// The size of the output data. - /// - /// If output_data is NULL this MUST be 0. - size_t output_size; - - /// The pointer to a function releasing all resources associated with - /// the result object. - /// - /// This function pointer is optional (MAY be NULL) and MAY be set by - /// the EVM implementation. If set it MUST be used by the user to - /// release memory and other resources associated with the result object. - /// After the result resources are released the result object - /// MUST NOT be used any more. - /// - /// The suggested code pattern for releasing EVM results: - /// @code - /// struct evm_result result = ...; - /// if (result.release) - /// result.release(&result); - /// @endcode - /// - /// @note - /// It works similarly to C++ virtual destructor. Attaching the release - /// function to the result itself allows EVM composition. - evm_release_result_fn release; - - /// The address of the contract created by CREATE opcode. - /// - /// This field has valid value only if the result describes successful - /// CREATE (evm_result::status_code is ::EVM_SUCCESS). - struct evm_address create_address; - - /// Reserved data that MAY be used by a evm_result object creator. - /// - /// This reserved 4 bytes together with 20 bytes from create_address form - /// 24 bytes of memory called "optional data" within evm_result struct - /// to be optionally used by the evm_result object creator. - /// - /// @see evm_result_optional_data, evm_get_optional_data(). - /// - /// Also extends the size of the evm_result to 64 bytes (full cache line). - uint8_t padding[4]; -}; - - -/// The union representing evm_result "optional data". -/// -/// The evm_result struct contains 24 bytes of optional data that can be -/// reused by the obejct creator if the object does not contain -/// evm_result::create_address. -/// -/// An EVM implementation MAY use this memory to keep additional data -/// when returning result from ::evm_execute_fn. -/// The host application MAY use this memory to keep additional data -/// when returning result of performed calls from ::evm_call_fn. -/// -/// @see evm_get_optional_data(), evm_get_const_optional_data(). -union evm_result_optional_data -{ - uint8_t bytes[24]; - void* pointer; -}; - -/// Provides read-write access to evm_result "optional data". -static inline union evm_result_optional_data* evm_get_optional_data( - struct evm_result* result) -{ - return (union evm_result_optional_data*) &result->create_address; -} - -/// Provides read-only access to evm_result "optional data". -static inline const union evm_result_optional_data* evm_get_const_optional_data( - const struct evm_result* result) -{ - return (const union evm_result_optional_data*) &result->create_address; -} - - -/// Check account existence callback function -/// -/// This callback function is used by the EVM to check if -/// there exists an account at given address. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the account the query is about. -/// @return 1 if exists, 0 otherwise. -typedef int (*evm_account_exists_fn)(struct evm_context* context, - const struct evm_address* address); - -/// Get storage callback function. -/// -/// This callback function is used by an EVM to query the given contract -/// storage entry. -/// @param[out] result The returned storage value. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the contract. -/// @param key The index of the storage entry. -typedef void (*evm_get_storage_fn)(struct evm_uint256be* result, - struct evm_context* context, - const struct evm_address* address, - const struct evm_uint256be* key); - -/// Set storage callback function. -/// -/// This callback function is used by an EVM to update the given contract -/// storage entry. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the contract. -/// @param key The index of the storage entry. -/// @param value The value to be stored. -typedef void (*evm_set_storage_fn)(struct evm_context* context, - const struct evm_address* address, - const struct evm_uint256be* key, - const struct evm_uint256be* value); - -/// Get balance callback function. -/// -/// This callback function is used by an EVM to query the balance of the given -/// address. -/// @param[out] result The returned balance value. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address. -typedef void (*evm_get_balance_fn)(struct evm_uint256be* result, - struct evm_context* context, - const struct evm_address* address); - -/// Get code callback function. -/// -/// This callback function is used by an EVM to get the code of a contract of -/// given address. -/// -/// @param[out] result_code The pointer to the contract code. This argument is -/// optional. If NULL is provided, the host MUST only -/// return the code size. It will be freed by the Client. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the contract. -/// @return The size of the code. -typedef size_t (*evm_get_code_fn)(const uint8_t** result_code, - struct evm_context* context, - const struct evm_address* address); - -/// Selfdestruct callback function. -/// -/// This callback function is used by an EVM to SELFDESTRUCT given contract. -/// The execution of the contract will not be stopped, that is up to the EVM. -/// -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the contract to be selfdestructed. -/// @param beneficiary The address where the remaining ETH is going to be -/// transferred. -typedef void (*evm_selfdestruct_fn)(struct evm_context* context, - const struct evm_address* address, - const struct evm_address* beneficiary); - -/// Log callback function. -/// -/// This callback function is used by an EVM to inform about a LOG that happened -/// during an EVM bytecode execution. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param address The address of the contract that generated the log. -/// @param data The pointer to unindexed data attached to the log. -/// @param data_size The length of the data. -/// @param topics The pointer to the array of topics attached to the log. -/// @param topics_count The number of the topics. Valid values are between -/// 0 and 4 inclusively. -typedef void (*evm_emit_log_fn)(struct evm_context* context, - const struct evm_address* address, - const uint8_t* data, - size_t data_size, - const struct evm_uint256be topics[], - size_t topics_count); - -/// Pointer to the callback function supporting EVM calls. -/// -/// @param[out] result The result of the call. The result object is not -/// initialized by the EVM, the Client MUST correctly -/// initialize all expected fields of the structure. -/// @param context The pointer to the Host execution context. -/// @see ::evm_context. -/// @param msg Call parameters. @see ::evm_message. -typedef void (*evm_call_fn)(struct evm_result* result, - struct evm_context* context, - const struct evm_message* msg); - -/// The context interface. -/// -/// The set of all callback functions expected by EVM instances. This is C -/// realisation of vtable for OOP interface (only virtual methods, no data). -/// Host implementations SHOULD create constant singletons of this (similarly -/// to vtables) to lower the maintenance and memory management cost. -struct evm_context_fn_table { - evm_account_exists_fn account_exists; - evm_get_storage_fn get_storage; - evm_set_storage_fn set_storage; - evm_get_balance_fn get_balance; - evm_get_code_fn get_code; - evm_selfdestruct_fn selfdestruct; - evm_call_fn call; - evm_get_tx_context_fn get_tx_context; - evm_get_block_hash_fn get_block_hash; - evm_emit_log_fn emit_log; -}; - - -/// Execution context managed by the Host. -/// -/// The Host MUST pass the pointer to the execution context to -/// ::evm_execute_fn. The EVM MUST pass the same pointer back to the Host in -/// every callback function. -/// The context MUST contain at least the function table defining the context -/// callback interface. -/// Optionally, The Host MAY include in the context additional data. -struct evm_context { - - /// Function table defining the context interface (vtable). - const struct evm_context_fn_table* fn_table; -}; - - -struct evm_instance; ///< Forward declaration. - -/// Destroys the EVM instance. -/// -/// @param evm The EVM instance to be destroyed. -typedef void (*evm_destroy_fn)(struct evm_instance* evm); - - -/// Configures the EVM instance. -/// -/// Allows modifying options of the EVM instance. -/// Options: -/// - code cache behavior: on, off, read-only, ... -/// - optimizations, -/// -/// @param evm The EVM instance to be configured. -/// @param name The option name. NULL-terminated string. Cannot be NULL. -/// @param value The new option value. NULL-terminated string. Cannot be NULL. -/// @return 1 if the option set successfully, 0 otherwise. -typedef int (*evm_set_option_fn)(struct evm_instance* evm, - char const* name, - char const* value); - - -/// EVM revision. -/// -/// The revision of the EVM specification based on the Ethereum -/// upgrade / hard fork codenames. -enum evm_revision { - EVM_FRONTIER = 0, - EVM_HOMESTEAD = 1, - EVM_TANGERINE_WHISTLE = 2, - EVM_SPURIOUS_DRAGON = 3, - EVM_BYZANTIUM = 4, - EVM_CONSTANTINOPLE = 5, -}; - - -/// Generates and executes machine code for given EVM bytecode. -/// -/// All the fun is here. This function actually does something useful. -/// -/// @param instance A EVM instance. -/// @param context The pointer to the Host execution context to be passed -/// to callback functions. @see ::evm_context. -/// @param rev Requested EVM specification revision. -/// @param msg Call parameters. @see ::evm_message. -/// @param code Reference to the bytecode to be executed. -/// @param code_size The length of the bytecode. -/// @return All execution results. -typedef struct evm_result (*evm_execute_fn)(struct evm_instance* instance, - struct evm_context* context, - enum evm_revision rev, - const struct evm_message* msg, - uint8_t const* code, - size_t code_size); - - -/// The EVM instance. -/// -/// Defines the base struct of the EVM implementation. -struct evm_instance { - - /// EVM-C ABI version implemented by the EVM instance. - /// - /// For future use to detect ABI incompatibilities. The EVM-C ABI version - /// represented by this file is in ::EVM_ABI_VERSION. - /// - /// @todo Consider removing this field. - const int abi_version; - - /// Pointer to function destroying the EVM instance. - evm_destroy_fn destroy; - - /// Pointer to function executing a code by the EVM instance. - evm_execute_fn execute; - - /// Optional pointer to function modifying VM's options. - /// - /// If the VM does not support this feature the pointer can be NULL. - evm_set_option_fn set_option; -}; - -// END Python CFFI declarations - -/// Example of a function creating an instance of an example EVM implementation. -/// -/// Each EVM implementation MUST provide a function returning an EVM instance. -/// The function SHOULD be named `_create(void)`. -/// -/// @return EVM instance or NULL indicating instance creation failure. -struct evm_instance* examplevm_create(void); - - -#if __cplusplus -} -#endif - -#endif // EVM_H -/// @} diff --git a/evmc b/evmc new file mode 160000 index 000000000..93fb8687e --- /dev/null +++ b/evmc @@ -0,0 +1 @@ +Subproject commit 93fb8687e6aec56a688b47f200404a799bc9e889 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3cdca5840..076707fc1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,6 @@ find_package(Threads REQUIRED) add_library(hera eei.cpp eei.h hera.cpp hera.h - ${evm_include_dir}/evm.h ) option(HERA_DEBUGGING "Display debugging messages during execution." ON) @@ -25,7 +24,7 @@ if(HERA_EVM2WASM) endif() target_include_directories(hera PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${evm_include_dir}) -target_link_libraries(hera PRIVATE binaryen::binaryen Threads::Threads) +target_link_libraries(hera PRIVATE evmc binaryen::binaryen Threads::Threads) if(NOT WIN32) if(CMAKE_COMPILER_IS_GNUCXX) set_target_properties(hera PROPERTIES LINK_FLAGS "-Wl,--no-undefined") diff --git a/src/eei.cpp b/src/eei.cpp index e446559dd..092befdbd 100644 --- a/src/eei.cpp +++ b/src/eei.cpp @@ -49,7 +49,7 @@ namespace HeraVM { namespace { #if HERA_DEBUGGING -string toHex(evm_uint256be const& value) { +string toHex(evmc_uint256be const& value) { ostringstream os; os << hex; for (auto b: value.bytes) @@ -118,7 +118,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("printStorage") || import->base == Name("printStorageHex")) { uint32_t pathOffset = arguments[0].geti32(); - evm_uint256be path = loadUint256(pathOffset); + evmc_uint256be path = loadUint256(pathOffset); bool useHex = import->base == Name("printStorageHex"); @@ -130,7 +130,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "): " << dec; - evm_uint256be result; + evmc_uint256be result; context->fn_table->get_storage(&result, context, &msg.destination, &path); if (useHex) @@ -158,7 +158,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "evmTrace\n"; - static constexpr int stackItemSize = sizeof(evm_uint256be); + static constexpr int stackItemSize = sizeof(evmc_uint256be); heraAssert(sp <= (1024 * stackItemSize), "EVM stack pointer out of bounds."); cout << "{\"depth\":" << dec << msg.depth @@ -171,7 +171,7 @@ string toHex(evm_uint256be const& value) { for (int32_t i = sp; i >= 0; i -= stackItemSize) { if (i != sp) cout << ','; - evm_uint256be x = loadUint256(static_cast(i)); + evmc_uint256be x = loadUint256(static_cast(i)); cout << '"' << toHex(x) << '"'; } cout << "]}" << endl; @@ -230,8 +230,8 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getBalance " << hex << addressOffset << " " << resultOffset << dec << "\n"; - evm_address address = loadUint160(addressOffset); - evm_uint256be result; + evmc_address address = loadUint160(addressOffset); + evmc_uint256be result; takeGas(GasSchedule::balance); context->fn_table->get_balance(&result, context, &address); @@ -246,7 +246,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getBlockHash " << hex << number << " " << resultOffset << dec << "\n"; - evm_uint256be blockhash; + evmc_uint256be blockhash; takeGas(GasSchedule::blockhash); context->fn_table->get_block_hash(&blockhash, context, number); @@ -339,16 +339,17 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "externalCodeCopy " << hex << addressOffset << " " << resultOffset << " " << codeOffset << " " << length << dec << "\n"; - evm_address address = loadUint160(addressOffset); - const uint8_t *code; - size_t code_size = context->fn_table->get_code(&code, context, &address); - heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow"); heraAssert(numeric_limits::max() - GasSchedule::extcode >= GasSchedule::copy * ((uint64_t(length) + 31) / 32), "Gas charge overflow"); takeGas(GasSchedule::extcode + GasSchedule::copy * ((uint64_t(length) + 31) / 32)); - // NOTE: code will be freed by the callee (client) + + evmc_address address = loadUint160(addressOffset); // FIXME: optimise this so not vector needs to be created - storeMemory(vector(code, code + code_size), codeOffset, resultOffset, length); + vector codeBuffer(length); + size_t numCopied = context->fn_table->copy_code(context, &address, codeOffset, codeBuffer.data(), codeBuffer.size()); + fill_n(&codeBuffer[numCopied], length - numCopied, 0); + + storeMemory(codeBuffer, codeOffset, resultOffset, length); return Literal(); } @@ -358,9 +359,9 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getExternalCodeSize " << hex << addressOffset << dec << "\n"; - evm_address address = loadUint160(addressOffset); + evmc_address address = loadUint160(addressOffset); takeGas(GasSchedule::extcode); - size_t code_size = context->fn_table->get_code(NULL, context, &address); + size_t code_size = context->fn_table->get_code_size(context, &address); return Literal(static_cast(code_size)); } @@ -370,7 +371,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getBlockCoinbase " << hex << resultOffset << dec << "\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -384,7 +385,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getBlockDifficulty " << hex << offset << dec << "\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -396,7 +397,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockGasLimit")) { HERA_DEBUG << "getBlockGasLimit\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -411,7 +412,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getTxGasPrice " << hex << valueOffset << dec << "\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -427,12 +428,12 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "log " << hex << dataOffset << " " << length << " " << numberOfTopics << dec << "\n"; - if (msg.flags & EVM_STATIC) + if (msg.flags & EVMC_STATIC) throw StaticModeViolation{"log"}; heraAssert(numberOfTopics <= 4, "Too many topics specified"); - array topics; + array topics; for (size_t i = 0; i < numberOfTopics; ++i) { uint32_t topicOffset = arguments[3 + i].geti32(); topics[i] = loadUint256(topicOffset); @@ -455,7 +456,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockNumber")) { HERA_DEBUG << "getBlockNumber\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -468,7 +469,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockTimestamp")) { HERA_DEBUG << "getBlockTimestamp\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -483,7 +484,7 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "getTxOrigin " << hex << resultOffset << dec << "\n"; - evm_tx_context tx_context; + evmc_tx_context tx_context; takeGas(GasSchedule::base); context->fn_table->get_tx_context(&tx_context, context); @@ -498,12 +499,12 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "storageStore " << hex << pathOffset << " " << valueOffset << dec << "\n"; - if (msg.flags & EVM_STATIC) + if (msg.flags & EVMC_STATIC) throw StaticModeViolation{"storageStore"}; - evm_uint256be path = loadUint256(pathOffset); - evm_uint256be value = loadUint256(valueOffset); - evm_uint256be current; + evmc_uint256be path = loadUint256(pathOffset); + evmc_uint256be value = loadUint256(valueOffset); + evmc_uint256be current; context->fn_table->get_storage(¤t, context, &msg.destination, &path); @@ -525,8 +526,8 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "storageLoad " << hex << pathOffset << " " << resultOffset << dec << "\n"; - evm_uint256be path = loadUint256(pathOffset); - evm_uint256be result; + evmc_uint256be path = loadUint256(pathOffset); + evmc_uint256be result; takeGas(GasSchedule::storageLoad); context->fn_table->get_storage(&result, context, &msg.destination, &path); @@ -583,9 +584,9 @@ string toHex(evm_uint256be const& value) { uint32_t dataOffset; uint32_t dataLength; - heraAssert((msg.flags & ~EVM_STATIC) == 0, "Unknown flags not supported."); + heraAssert((msg.flags & ~EVMC_STATIC) == 0, "Unknown flags not supported."); - evm_message call_message; + evmc_message call_message; call_message.destination = loadUint160(addressOffset); call_message.flags = msg.flags; call_message.code_hash = {}; @@ -599,9 +600,9 @@ string toHex(evm_uint256be const& value) { call_message.sender = msg.destination; call_message.value = loadUint128(valueOffset); - call_message.kind = (import->base == Name("callCode")) ? EVM_CALLCODE : EVM_CALL; + call_message.kind = (import->base == Name("callCode")) ? EVMC_CALLCODE : EVMC_CALL; - if ((msg.flags & EVM_STATIC) && import->base == Name("call") && !isZeroUint256(call_message.value)) + if ((msg.flags & EVMC_STATIC) && import->base == Name("call") && !isZeroUint256(call_message.value)) throw StaticModeViolation{"call"}; ensureSenderBalance(call_message.value); @@ -613,12 +614,12 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("callDelegate")) { call_message.sender = msg.sender; call_message.value = msg.value; - call_message.kind = EVM_DELEGATECALL; + call_message.kind = EVMC_DELEGATECALL; } else if (import->base == Name("callStatic")) { call_message.sender = msg.destination; call_message.value = {}; - call_message.kind = EVM_CALL; - call_message.flags |= EVM_STATIC; + call_message.kind = EVMC_CALL; + call_message.flags |= EVMC_STATIC; } } @@ -640,7 +641,7 @@ string toHex(evm_uint256be const& value) { call_message.input_size = 0; } - evm_result call_result; + evmc_result call_result; if (import->base == Name("call") && !context->fn_table->account_exists(context, &call_message.destination)) takeGas(GasSchedule::callNewAccount); @@ -663,9 +664,9 @@ string toHex(evm_uint256be const& value) { result.gasLeft += call_result.gas_left; switch (call_result.status_code) { - case EVM_SUCCESS: + case EVMC_SUCCESS: return Literal(uint32_t(0)); - case EVM_REVERT: + case EVMC_REVERT: return Literal(uint32_t(2)); default: return Literal(uint32_t(1)); @@ -680,10 +681,10 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "create " << hex << valueOffset << " " << dataOffset << " " << length << dec << " " << resultOffset << dec << "\n"; - if (msg.flags & EVM_STATIC) + if (msg.flags & EVMC_STATIC) throw StaticModeViolation{"create"}; - evm_message create_message; + evmc_message create_message; create_message.destination = {}; create_message.sender = msg.destination; @@ -704,16 +705,16 @@ string toHex(evm_uint256be const& value) { create_message.code_hash = {}; create_message.gas = result.gasLeft - (result.gasLeft / 64); create_message.depth = msg.depth + 1; - create_message.kind = EVM_CREATE; + create_message.kind = EVMC_CREATE; create_message.flags = 0; - evm_result create_result; + evmc_result create_result; takeGas(create_message.gas); takeGas(GasSchedule::create); context->fn_table->call(&create_result, context, &create_message); - if (create_result.status_code == EVM_SUCCESS) { + if (create_result.status_code == EVMC_SUCCESS) { storeUint160(create_result.create_address, resultOffset); lastReturnData.clear(); } else if (create_result.output_data) { @@ -726,9 +727,9 @@ string toHex(evm_uint256be const& value) { create_result.release(&create_result); switch (create_result.status_code) { - case EVM_SUCCESS: + case EVMC_SUCCESS: return Literal(uint32_t(0)); - case EVM_REVERT: + case EVMC_REVERT: return Literal(uint32_t(2)); default: return Literal(uint32_t(1)); @@ -740,10 +741,10 @@ string toHex(evm_uint256be const& value) { HERA_DEBUG << "selfDestruct " << hex << addressOffset << dec << "\n"; - if (msg.flags & EVM_STATIC) + if (msg.flags & EVMC_STATIC) throw StaticModeViolation{"selfDestruct"}; - evm_address address = loadUint160(addressOffset); + evmc_address address = loadUint160(addressOffset); if (!context->fn_table->account_exists(context, &address)) takeGas(GasSchedule::callNewAccount); @@ -827,38 +828,38 @@ string toHex(evm_uint256be const& value) { * Memory Op Wrapper Functions */ - evm_uint256be EthereumInterface::loadUint256(uint32_t srcOffset) + evmc_uint256be EthereumInterface::loadUint256(uint32_t srcOffset) { - evm_uint256be dst = {}; + evmc_uint256be dst = {}; loadMemory(srcOffset, dst.bytes, 32); return dst; } - void EthereumInterface::storeUint256(evm_uint256be const& src, uint32_t dstOffset) + void EthereumInterface::storeUint256(evmc_uint256be const& src, uint32_t dstOffset) { storeMemory(src.bytes, dstOffset, 32); } - evm_address EthereumInterface::loadUint160(uint32_t srcOffset) + evmc_address EthereumInterface::loadUint160(uint32_t srcOffset) { - evm_address dst = {}; + evmc_address dst = {}; loadMemory(srcOffset, dst.bytes, 20); return dst; } - void EthereumInterface::storeUint160(evm_address const& src, uint32_t dstOffset) + void EthereumInterface::storeUint160(evmc_address const& src, uint32_t dstOffset) { storeMemory(src.bytes, dstOffset, 20); } - evm_uint256be EthereumInterface::loadUint128(uint32_t srcOffset) + evmc_uint256be EthereumInterface::loadUint128(uint32_t srcOffset) { - evm_uint256be dst = {}; + evmc_uint256be dst = {}; loadMemory(srcOffset, dst.bytes, 16); return dst; } - void EthereumInterface::storeUint128(evm_uint256be const& src, uint32_t dstOffset) + void EthereumInterface::storeUint128(evmc_uint256be const& src, uint32_t dstOffset) { heraAssert(!exceedsUint128(src), "Value at src cannot exceed 2^128-1"); storeMemory(src.bytes + 16, dstOffset, 16); @@ -867,15 +868,15 @@ string toHex(evm_uint256be const& value) { /* * Utilities */ - void EthereumInterface::ensureSenderBalance(evm_uint256be const& value) + void EthereumInterface::ensureSenderBalance(evmc_uint256be const& value) { - evm_uint256be balance; + evmc_uint256be balance; context->fn_table->get_balance(&balance, context, &msg.destination); if (safeLoadUint64(balance) < safeLoadUint64(value)) throw OutOfGasException{}; } - uint64_t EthereumInterface::safeLoadUint64(evm_uint256be const& value) + uint64_t EthereumInterface::safeLoadUint64(evmc_uint256be const& value) { heraAssert(!exceedsUint64(value), "Value exceeds 64 bits."); uint64_t ret = 0; @@ -886,7 +887,7 @@ string toHex(evm_uint256be const& value) { return ret; } - bool EthereumInterface::exceedsUint64(evm_uint256be const& value) + bool EthereumInterface::exceedsUint64(evmc_uint256be const& value) { for (unsigned i = 0; i < 24; i++) { if (value.bytes[i]) @@ -895,7 +896,7 @@ string toHex(evm_uint256be const& value) { return false; } - bool EthereumInterface::exceedsUint128(evm_uint256be const& value) + bool EthereumInterface::exceedsUint128(evmc_uint256be const& value) { for (unsigned i = 0; i < 16; i++) { if (value.bytes[i]) @@ -904,7 +905,7 @@ string toHex(evm_uint256be const& value) { return false; } - bool EthereumInterface::isZeroUint256(evm_uint256be const& value) + bool EthereumInterface::isZeroUint256(evmc_uint256be const& value) { for (unsigned i = 0; i < 32; i++) { if (value.bytes[i] != 0) diff --git a/src/eei.h b/src/eei.h index a3bdb1ce2..6bab4e894 100644 --- a/src/eei.h +++ b/src/eei.h @@ -26,7 +26,7 @@ #include #include -#include "evm.h" +#include #include "shell-interface.h" #include "hera.h" #include "exceptions.h" @@ -43,9 +43,9 @@ struct ExecutionResult { struct EthereumInterface : ShellExternalInterface { EthereumInterface( - evm_context* _context, + evmc_context* _context, std::vector const& _code, - evm_message const& _msg, + evmc_message const& _msg, ExecutionResult & _result ): ShellExternalInterface(), @@ -74,29 +74,29 @@ struct EthereumInterface : ShellExternalInterface { void storeMemory(const uint8_t *src, uint32_t dstOffset, uint32_t length); void storeMemory(std::vector const& src, uint32_t srcOffset, uint32_t dstOffset, uint32_t length); - evm_uint256be loadUint256(uint32_t srcOffset); - void storeUint256(evm_uint256be const& src, uint32_t dstOffset); - evm_address loadUint160(uint32_t srcOffset); - void storeUint160(evm_address const& src, uint32_t dstOffset); - evm_uint256be loadUint128(uint32_t srcOffset); - void storeUint128(evm_uint256be const& src, uint32_t dstOffset); + evmc_uint256be loadUint256(uint32_t srcOffset); + void storeUint256(evmc_uint256be const& src, uint32_t dstOffset); + evmc_address loadUint160(uint32_t srcOffset); + void storeUint160(evmc_address const& src, uint32_t dstOffset); + evmc_uint256be loadUint128(uint32_t srcOffset); + void storeUint128(evmc_uint256be const& src, uint32_t dstOffset); - void ensureSenderBalance(evm_uint256be const& value); + void ensureSenderBalance(evmc_uint256be const& value); - static uint64_t safeLoadUint64(evm_uint256be const& value); + static uint64_t safeLoadUint64(evmc_uint256be const& value); /* Checks if host supplied 256 bit value exceeds UINT64_MAX */ - static bool exceedsUint64(evm_uint256be const& value); + static bool exceedsUint64(evmc_uint256be const& value); /* Checks if host supplied 256 bit value exceeds UINT128_MAX */ - static bool exceedsUint128(evm_uint256be const& value); + static bool exceedsUint128(evmc_uint256be const& value); /* Checks if 256 bit value is all zeroes */ - static bool isZeroUint256(evm_uint256be const& value); + static bool isZeroUint256(evmc_uint256be const& value); - evm_context* context = nullptr; + evmc_context* context = nullptr; std::vector const& code; - evm_message const& msg; + evmc_message const& msg; std::vector lastReturnData; ExecutionResult & result; }; diff --git a/src/hera.cpp b/src/hera.cpp index a3b81174a..7cdcc2bf3 100644 --- a/src/hera.cpp +++ b/src/hera.cpp @@ -37,7 +37,8 @@ #include #include -#include "evm.h" +#include + #include "hera.h" #include "eei.h" #include "exceptions.h" @@ -46,14 +47,14 @@ using namespace std; using namespace wasm; using namespace HeraVM; -struct hera_instance : evm_instance { +struct hera_instance : evmc_instance { bool fallback = false; #if HERA_EVM2WASM bool use_evm2wasm_js = false; bool use_evm2wasm_js_trace = false; #endif - hera_instance() : evm_instance({EVM_ABI_VERSION, nullptr, nullptr, nullptr}) {} + hera_instance() : evmc_instance({EVMC_ABI_VERSION, nullptr, nullptr, nullptr}) {} }; namespace { @@ -73,12 +74,12 @@ bool hasWasmPreamble(vector const& _input) { #if HERA_METERING_CONTRACT || HERA_EVM2WASM vector callSystemContract( - evm_context* context, - evm_address const& address, + evmc_context* context, + evmc_address const& address, int64_t & gas, vector const& input ) { - evm_message message = { + evmc_message message = { .destination = address, .sender = {}, .value = {}, @@ -87,15 +88,15 @@ vector callSystemContract( .code_hash = {}, .gas = gas, .depth = 0, - .kind = EVM_CALL, - .flags = EVM_STATIC + .kind = EVMC_CALL, + .flags = EVMC_STATIC }; - evm_result result; + evmc_result result; context->fn_table->call(&result, context, &message); vector ret; - if (result.status_code == EVM_SUCCESS && result.output_data) + if (result.status_code == EVMC_SUCCESS && result.output_data) ret.assign(result.output_data, result.output_data + result.output_size); gas = result.gas_left; @@ -107,7 +108,7 @@ vector callSystemContract( } #endif -vector sentinel(evm_context* context, vector const& input) +vector sentinel(evmc_context* context, vector const& input) { #if HERA_DEBUGGING cerr << "Metering (input " << input.size() << " bytes)..." << endl; @@ -197,7 +198,7 @@ vector evm2wasm_js(vector const& input, bool evmTrace) { return vector(str.begin(), str.end()); } -vector evm2wasm(evm_context* context, vector const& input) { +vector evm2wasm(evmc_context* context, vector const& input) { #if HERA_DEBUGGING cerr << "Calling evm2wasm (input " << input.size() << " bytes)..." << endl; #endif @@ -220,10 +221,10 @@ vector evm2wasm(evm_context* context, vector const& input) { #endif void execute( - evm_context* context, - vector const& code, - evm_message const& msg, - ExecutionResult & result + evmc_context* context, + vector const& code, + evmc_message const& msg, + ExecutionResult & result ) { #if HERA_DEBUGGING cerr << "Executing..." << endl; @@ -268,21 +269,21 @@ void execute( instance.callExport(main, args); } -void evm_destroy_result(evm_result const* result) +void hera_destroy_result(evmc_result const* result) { delete[] result->output_data; } -evm_result evm_execute( - evm_instance* instance, - evm_context* context, - enum evm_revision rev, - const evm_message* msg, - const uint8_t* code, - size_t code_size) -{ - evm_result ret; - memset(&ret, 0, sizeof(evm_result)); +evmc_result hera_execute( + evmc_instance *instance, + evmc_context *context, + enum evmc_revision rev, + const evmc_message *msg, + const uint8_t *code, + size_t code_size +) { + evmc_result ret; + memset(&ret, 0, sizeof(evmc_result)); try { heraAssert(msg->gas >= 0, "Negative startgas?"); @@ -303,14 +304,14 @@ evm_result evm_execute( _code = evm2wasm(context, _code); heraAssert(_code.size() != 0, "Transcompiling via evm2wasm failed"); #else - ret.status_code = hera->fallback ? EVM_REJECTED : EVM_FAILURE; + ret.status_code = hera->fallback ? EVMC_REJECTED : EVMC_FAILURE; return ret; #endif } - heraAssert(rev == EVM_BYZANTIUM, "Only Byzantium supported."); + heraAssert(rev == EVMC_BYZANTIUM, "Only Byzantium supported."); - if (msg->kind == EVM_CREATE) { + if (msg->kind == EVMC_CREATE) { // Meter the deployment (constructor) code _code = sentinel(context, _code); heraAssert(_code.size() > 5, "Invalid contract or metering failed."); @@ -322,7 +323,7 @@ evm_result evm_execute( if (result.returnValue.size() > 0) { vector returnValue; - if (msg->kind == EVM_CREATE && !result.isRevert && hasWasmPreamble(result.returnValue)) { + if (msg->kind == EVMC_CREATE && !result.isRevert && hasWasmPreamble(result.returnValue)) { // Meter the deployed code returnValue = sentinel(context, result.returnValue); heraAssert(returnValue.size() > 5, "Invalid contract or metering failed."); @@ -335,32 +336,32 @@ evm_result evm_execute( ret.output_size = returnValue.size(); ret.output_data = output_data; - ret.release = evm_destroy_result; + ret.release = hera_destroy_result; } - ret.status_code = result.isRevert ? EVM_REVERT : EVM_SUCCESS; + ret.status_code = result.isRevert ? EVMC_REVERT : EVMC_SUCCESS; ret.gas_left = result.gasLeft; } catch (OutOfGasException const&) { - ret.status_code = EVM_OUT_OF_GAS; + ret.status_code = EVMC_OUT_OF_GAS; } catch (StaticModeViolation const& e) { - ret.status_code = EVM_STATIC_MODE_ERROR; + ret.status_code = EVMC_STATIC_MODE_ERROR; #if HERA_DEBUGGING cerr << e.what() << endl; #endif } catch (InternalErrorException const& e) { // TODO: split exceptions properly, but for now this mostly (>90%) covers // issues in the contracts, as opposed to unrecoverable issues in Hera - ret.status_code = EVM_FAILURE; + ret.status_code = EVMC_FAILURE; #if HERA_DEBUGGING cerr << "InternalError: " << e.what() << endl; #endif } catch (exception const& e) { - ret.status_code = EVM_INTERNAL_ERROR; + ret.status_code = EVMC_INTERNAL_ERROR; #if HERA_DEBUGGING cerr << "Unknown exception: " << e.what() << endl; #endif } catch (...) { - ret.status_code = EVM_INTERNAL_ERROR; + ret.status_code = EVMC_INTERNAL_ERROR; #if HERA_DEBUGGING cerr << "Totally unknown exception" << endl; #endif @@ -369,10 +370,10 @@ evm_result evm_execute( return ret; } -int evm_set_option( - evm_instance* instance, - char const* name, - char const* value +int hera_set_option( + evmc_instance *instance, + char const *name, + char const *value ) { hera_instance* hera = static_cast(instance); if (strcmp(name, "fallback") == 0) { @@ -393,7 +394,7 @@ int evm_set_option( return 0; } -void evm_destroy(evm_instance* instance) +void hera_destroy(evmc_instance* instance) { hera_instance* hera = static_cast(instance); delete hera; @@ -403,13 +404,13 @@ void evm_destroy(evm_instance* instance) extern "C" { -evm_instance* hera_create() +evmc_instance* hera_create() { hera_instance* instance = new hera_instance; - instance->destroy = evm_destroy; - instance->execute = evm_execute; - instance->set_option = evm_set_option; - return static_cast(instance); + instance->destroy = hera_destroy; + instance->execute = hera_execute; + instance->set_option = hera_set_option; + return static_cast(instance); } } diff --git a/src/hera.h b/src/hera.h index 5fa830859..bd0647c17 100644 --- a/src/hera.h +++ b/src/hera.h @@ -43,7 +43,7 @@ extern "C" { struct evm_instance; HERA_EXPORT -struct evm_instance* hera_create(void); +struct evmc_instance* hera_create(void); #if __cplusplus }