From fd360e3c87ba931261437cc4ec39747718be91d5 Mon Sep 17 00:00:00 2001 From: mratsim Date: Fri, 2 Feb 2018 10:44:16 +0100 Subject: [PATCH] Initial commit --- .gitignore | 5 + LICENSE | 21 ++ README.md | 3 + headers/evm.h | 553 +++++++++++++++++++++++++++++++++++++++++++++++ headers/evmjit.h | 27 +++ src/evm.nim | 468 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 1077 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 headers/evm.h create mode 100644 headers/evmjit.h create mode 100644 src/evm.nim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e3fffa --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Nim +nimcache/ + +# Executables shall be put in an ignored build/ directory +build/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8766e65 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Status Research & Development GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b63e7e9 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Nim Ethereum EVM JIT + +Nim EVM JIT is a wrapper for EVM JIT \ No newline at end of file diff --git a/headers/evm.h b/headers/evm.h new file mode 100644 index 0000000..8ee6258 --- /dev/null +++ b/headers/evm.h @@ -0,0 +1,553 @@ +/// 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 +/// @{ + +#ifdef C2NIM +# dynlib libevmjit +# cdecl +# if defined(windows) +# define libevmjit "librocksdb.dll" +# elif defined(macosx) +# define libevmjit "librocksdb.dylib" +# else +# define libevmjit "librocksdb.so" +# endif +# mangle uint32_t uint32 +# mangle uint16_t uint16 +# mangle uint8_t uint8 +# mangle uint64_t uint64 +# mangle int32_t int32 +# mangle int16_t int16 +# mangle int8_t int8 +# mangle int64_t int64 +# mangle cuchar uint8 +#endif + +#ifndef EVM_H +#define EVM_H + +#include // Definition of int64_t, uint64_t. +#include // Definition of size_t. + +#ifdef __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); + + +#ifdef __cplusplus +} +#endif + +#endif // EVM_H +/// @} diff --git a/headers/evmjit.h b/headers/evmjit.h new file mode 100644 index 0000000..b1d1dcf --- /dev/null +++ b/headers/evmjit.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#ifdef _MSC_VER +#ifdef evmjit_EXPORTS +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +#else +#define EXPORT __attribute__ ((visibility ("default"))) +#endif + +#if __cplusplus +extern "C" { +#endif + +/// Create EVMJIT instance. +/// +/// @return The EVMJIT instance. +EXPORT struct evm_instance* evmjit_create(void); + +#if __cplusplus +} +#endif diff --git a/src/evm.nim b/src/evm.nim new file mode 100644 index 0000000..49a603f --- /dev/null +++ b/src/evm.nim @@ -0,0 +1,468 @@ +## 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 +## @{ + +{.deadCodeElim: on.} +when defined(windows): + const + libevmjit* = "librocksdb.dll" +elif defined(macosx): + const + libevmjit* = "librocksdb.dylib" +else: + const + libevmjit* = "librocksdb.so" +## BEGIN Python CFFI declarations + +const + ## The EVM-C ABI version number of the interface declared in this file. + EVM_ABI_VERSION* = 0 + + + +type + evm_uint256be* {.bycopy.} = object + ## 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. + bytes*: array[32, uint8] ## The 32 bytes of the big-endian integer or hash. + + evm_address* {.bycopy.} = object + ## Big-endian 160-bit hash suitable for keeping an Ethereum address. + bytes*: array[20, uint8] ## The 20 bytes of the hash. + + evm_call_kind* {.size: sizeof(cint).} = enum + ## The kind of call-like instruction. + 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. + + evm_flags* {.size: sizeof(cint).} = enum + ## The flags for ::evm_message. + EVM_STATIC = 1 + + evm_message* {.bycopy.} = object + ## The message describing an EVM call, + ## including a zero-depth calls from a transaction origin. + + destination*: evm_address + ## The destination of the message. + + sender*: evm_address + ## The sender of the message + + value*: evm_uint256be + ## The amount of Ether transferred with the message. + + input_data*: ptr uint8 + ## The message input data. This MAY be NULL. + + input_size*: csize + ## The size of the message input data. + ## If input_data is NULL this MUST be 0. + + code_hash*: evm_uint256be + ## The optional hash of the code of the destination account. + ## The null hash MUST be used when not specified. + + gas*: int64 + ## The amount of gas for message execution. + + depth*: int32 + ## The call depth. + + kind*: evm_call_kind + ## The kind of the call. For zero-depth calls ::EVM_CALL SHOULD be used. + + flags*: uint32 + ## Additional flags modifying the call execution behavior. + ## In the current version the only valid values are ::EVM_STATIC or 0. + + evm_tx_context* {.bycopy.} = object + ## The transaction and block data for execution. + tx_gas_price*: evm_uint256be ## The transaction gas price. + tx_origin*: evm_address ## The transaction origin account. + block_coinbase*: evm_address ## The miner of the block. + block_number*: int64 ## The block number. + block_timestamp*: int64 ## The block timestamp. + block_gas_limit*: int64 ## The block gas limit. + block_difficulty*: evm_uint256be ## The block difficulty. + + evm_get_tx_context_fn* = proc (result: ptr evm_tx_context; context: ptr evm_context) {.cdecl.} + ## 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. + + evm_get_block_hash_fn* = proc (result: ptr evm_uint256be; context: ptr evm_context; + number: int64) {.cdecl.} + ## 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. + + evm_status_code* {.size: sizeof(cint).} = enum + ## The execution status code. + EVM_INTERNAL_ERROR = -2, + ## 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_REJECTED = -1, + ## 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_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. + EVM_STATIC_MODE_ERROR = 8 ## Tried to execute an operation which is restricted in static mode. + + evm_release_result_fn* = proc (result: ptr evm_result) {.cdecl.} + ## 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. + + evm_result* {.bycopy.} = object + ## The EVM code execution result. + status_code*: evm_status_code ## The execution status code. + + gas_left*: int64 + ## The amount of gas left after the execution. + ## + ## If evm_result::code is not ::EVM_SUCCESS nor ::EVM_REVERT + ## the value MUST be 0. + + output_data*: ptr uint8 + ## 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. + + output_size*: csize + ## The size of the output data. + ## + ## If output_data is NULL this MUST be 0. + + release*: evm_release_result_fn + ## 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. + + create_address*: evm_address + ## 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). + + padding*: array[4, uint8] + ## 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). + + evm_result_optional_data* {.bycopy.} = object {.union.} + ## 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(). + bytes*: array[24, uint8] + pointer*: pointer + + evm_account_exists_fn* = proc (context: ptr evm_context; address: ptr evm_address): cint {.cdecl.} + ## 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. + + evm_get_storage_fn* = proc (result: ptr evm_uint256be; context: ptr evm_context; + address: ptr evm_address; key: ptr evm_uint256be) {.cdecl.} + ## 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. + + evm_set_storage_fn* = proc (context: ptr evm_context; address: ptr evm_address; + key: ptr evm_uint256be; value: ptr evm_uint256be) {.cdecl.} + ## 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. + + evm_get_balance_fn* = proc (result: ptr evm_uint256be; context: ptr evm_context; + address: ptr evm_address) {.cdecl.} + ## 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. + + evm_get_code_fn* = proc (result_code: ptr ptr uint8; context: ptr evm_context; + address: ptr evm_address): csize {.cdecl.} + ## 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. + + evm_selfdestruct_fn* = proc (context: ptr evm_context; address: ptr evm_address; + beneficiary: ptr evm_address) {.cdecl.} + ## 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. + + evm_emit_log_fn* = proc (context: ptr evm_context; address: ptr evm_address; + data: ptr uint8; data_size: csize; topics: ptr evm_uint256be; + topics_count: csize) {.cdecl.} + ## 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. + + evm_call_fn* = proc (result: ptr evm_result; context: ptr evm_context; + msg: ptr evm_message) {.cdecl.} + ## 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. + + evm_context_fn_table* {.bycopy.} = object + ## 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. + account_exists*: evm_account_exists_fn + get_storage*: evm_get_storage_fn + set_storage*: evm_set_storage_fn + get_balance*: evm_get_balance_fn + get_code*: evm_get_code_fn + selfdestruct*: evm_selfdestruct_fn + call*: evm_call_fn + get_tx_context*: evm_get_tx_context_fn + get_block_hash*: evm_get_block_hash_fn + emit_log*: evm_emit_log_fn + + evm_context* {.bycopy.} = object + ## 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. + fn_table*: ptr evm_context_fn_table ## Function table defining the context interface (vtable). + + evm_destroy_fn* = proc (evm: ptr evm_instance) {.cdecl.} + ## Forward declaration. + ## Destroys the EVM instance. + ## + ## @param evm The EVM instance to be destroyed. + + evm_set_option_fn* = proc (evm: ptr evm_instance; name: cstring; value: cstring): cint {.cdecl.} + ## 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. + + evm_revision* {.size: sizeof(cint).} = enum + ## EVM revision. + ## + ## The revision of the EVM specification based on the Ethereum + ## upgrade / hard fork codenames. + EVM_FRONTIER = 0, EVM_HOMESTEAD = 1, EVM_TANGERINE_WHISTLE = 2, + EVM_SPURIOUS_DRAGON = 3, EVM_BYZANTIUM = 4, EVM_CONSTANTINOPLE = 5 + + evm_execute_fn* = proc (instance: ptr evm_instance; context: ptr evm_context; + rev: evm_revision; msg: ptr evm_message; code: ptr uint8; + code_size: csize): evm_result {.cdecl.} + ## 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. + + evm_instance* {.bycopy.} = object + ## The EVM instance. + ## + ## Defines the base struct of the EVM implementation. + + abi_version*: cint + ## 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. + destroy*: evm_destroy_fn + ## Pointer to function destroying the EVM instance. + execute*: evm_execute_fn + ## Pointer to function executing a code by the EVM instance. + set_option*: evm_set_option_fn + ## Optional pointer to function modifying VM's options. + ## + ## If the VM does not support this feature the pointer can be NULL. + +proc evm_get_optional_data*(r: ptr evm_result): ptr evm_result_optional_data {.inline, cdecl.} = + ## Provides read-write access to evm_result "optional data". + return cast[ptr evm_result_optional_data](addr(r.create_address)) + +proc evm_get_const_optional_data*(r: ptr evm_result): ptr evm_result_optional_data {.inline, cdecl.} = + ## Provides read-only access to evm_result "optional data". + # TODO test writetracking: {.writes: [].} https://nim-lang.org/araq/writetracking.html + return cast[ptr evm_result_optional_data](addr(r.create_address)) + + +## 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); + +