From e2b77b11524f2ba910214c5dd0237d7a9bb3189e Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Sun, 28 Apr 2019 04:20:43 -0400 Subject: [PATCH 01/88] embedding eos-vm --- libraries/CMakeLists.txt | 3 ++ libraries/chain/CMakeLists.txt | 4 +- .../eosio/chain/webassembly/eos-vm.hpp | 32 ++++++++++++ libraries/chain/webassembly/eos-vm.cpp | 51 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp create mode 100644 libraries/chain/webassembly/eos-vm.cpp diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 54bb2f80e09..292e7021f46 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -14,6 +14,9 @@ set(RUN_RE2C OFF CACHE BOOL "Run re2c") set(WITH_EXCEPTIONS ON CACHE BOOL "Build with exceptions enabled" FORCE) add_subdirectory( wabt ) +set(USE_EXISTING_SOFTFLOAT 1) +add_subdirectory( eos-vm ) + set(ENABLE_STATIC ON) set(CMAKE_MACOSX_RPATH OFF) set(BUILD_ONLY_LIB ON CACHE BOOL "Library only build") diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 254d462c5ed..2b424855263 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -39,6 +39,7 @@ add_library( eosio_chain webassembly/wavm.cpp webassembly/wabt.cpp + webassembly/eos-vm.cpp # get_config.cpp # @@ -56,11 +57,12 @@ add_library( eosio_chain ) target_link_libraries( eosio_chain fc chainbase Logging IR WAST WASM Runtime - softfloat builtins wabt + softfloat builtins wabt eos-vm ) target_include_directories( eosio_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include" + "${CMAKE_CURRENT_SOURCE_DIR}/libraries/eos-vm/include" "${CMAKE_SOURCE_DIR}/libraries/wabt" "${CMAKE_BINARY_DIR}/libraries/wabt" ) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp new file mode 100644 index 00000000000..c60eac758a0 --- /dev/null +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include + +//eos-vm includes +#include + +namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { + +using namespace fc; +using namespace eosio::wasm_backend; +using namespace eosio::chain::webassembly::common; + +class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { + public: + eos_vm_runtime(); + std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; + + void immediately_exit_currently_running_module() override; + + private: + backend* _bkend; // non owning pointer to allow for immediate exit +}; + +#define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ + eosio::wasm_backend::registered_host_functions::add<&CLS::METHOD, eosio::wasm_backend::backend>(#MOD, #NAME); + +} } } }// eosio::chain::webassembly::wabt_runtime diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp new file mode 100644 index 00000000000..04882cb6fb3 --- /dev/null +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +//eos-vm includes +#include + +namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { + +using namespace eosio::wasm_backend; + +namespace wasm_constraints = eosio::chain::wasm_constraints; + +using backend_t = backend>; + +class eos_vm_instantiated_module : public wasm_instantiated_module_interface { + public: + + eos_vm_instantiated_module(std::unique_ptr mod) : + _instantiated_module(std::move(mod)) {} + + void apply(apply_context& context) override { + _instantiated_module->set_wasm_allocator( context.get_wasm_allocator() ); + if (!(const auto& res = _instantiated_module->run_start())) + EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); + + if (!(const auto& res = _instantiated_module(context.get_registered_host_functions(), "env", "apply", + (uint64_t)context.get_receiver(), + (uint64_t)context.get_action().account(), + (uint64_t)context.get_action().name)) + EOS_ASSERT(false, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); + } + + private: + std::unique_ptr _instantiated_module; +}; + +eos_vm_runtime::eos_vm_runtime() {} + +std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { + std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); + std::unique_ptr bkend = std::make_unique( cb ); + _bkend = bkend.get(); + return std::make_unique(std::move(bkend)); +} + +void eos_vm_runtime::immediately_exit_currently_running_module() { + _bkend->immediate_exit(); +} + +}}}} From 75b9ca6f22d77f6be70903862b13642475ad47a5 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Apr 2019 01:19:13 -0400 Subject: [PATCH 02/88] changes needed for new backend --- .../include/eosio/chain/wasm_interface.hpp | 9 +- .../eosio/chain/wasm_interface_private.hpp | 17 +++- .../eosio/chain/webassembly/common.hpp | 3 + .../eosio/chain/webassembly/eos-vm.hpp | 51 +++++++++- libraries/chain/wasm_interface.cpp | 96 ++++++++++--------- libraries/chain/webassembly/eos-vm.cpp | 31 +++--- .../testing/include/eosio/testing/tester.hpp | 2 + plugins/chain_plugin/chain_plugin.cpp | 2 +- 8 files changed, 139 insertions(+), 72 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 341331989e5..7c2bfa9eb75 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Runtime/Linker.h" #include "Runtime/Runtime.h" @@ -73,7 +74,8 @@ namespace eosio { namespace chain { public: enum class vm_type { wavm, - wabt + wabt, + eos_vm }; wasm_interface(vm_type vm, const chainbase::database& db); @@ -84,6 +86,9 @@ namespace eosio { namespace chain { //validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints static void validate(const controller& control, const bytes& code); + + //get the wasm_allocator used for the linear memory for wasm + static wasm_backend::wasm_allocator* get_wasm_allocator(); //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); @@ -108,4 +113,4 @@ namespace eosio{ namespace chain { std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime); }} -FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(wabt) ) +FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(wabt)(eos_vm) ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 45f70460b02..cf8df0afa9e 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -16,10 +17,14 @@ #include "WAST/WAST.h" #include "IR/Validate.h" +#include + using namespace fc; using namespace eosio::chain::webassembly; +using namespace eosio::wasm_backend; using namespace IR; using namespace Runtime; + using boost::multi_index_container; namespace eosio { namespace chain { @@ -42,6 +47,8 @@ namespace eosio { namespace chain { runtime_interface = std::make_unique(); else if(vm == wasm_interface::vm_type::wabt) runtime_interface = std::make_unique(); + else if(vm == wasm_interface::vm_type::eos_vm) + runtime_interface = std::make_unique(); else EOS_THROW(wasm_exception, "wasm_interface_impl fall through"); } @@ -54,6 +61,11 @@ namespace eosio { namespace chain { }); } + static wasm_allocator* get_wasm_allocator() { + thread_local wasm_allocator walloc; + return &walloc; + } + std::vector parse_initial_memory(const Module& module) { std::vector mem_image; @@ -167,8 +179,9 @@ namespace eosio { namespace chain { }; #define _REGISTER_INTRINSIC_EXPLICIT(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ - _REGISTER_WAVM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ - _REGISTER_WABT_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) + _REGISTER_WAVM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ + _REGISTER_WABT_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ + _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) #define _REGISTER_INTRINSIC4(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ _REGISTER_INTRINSIC_EXPLICIT(CLS, MOD, METHOD, WASM_SIG, NAME, SIG ) diff --git a/libraries/chain/include/eosio/chain/webassembly/common.hpp b/libraries/chain/include/eosio/chain/webassembly/common.hpp index 723bd3039b6..542d3c5b37e 100644 --- a/libraries/chain/include/eosio/chain/webassembly/common.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/common.hpp @@ -58,6 +58,8 @@ namespace eosio { namespace chain { */ template struct array_ptr { + using type = T; + array_ptr() = default; explicit array_ptr (T * value) : value(value) {} typename std::add_lvalue_reference::type operator*() const { @@ -80,6 +82,7 @@ namespace eosio { namespace chain { * class to represent an in-wasm-memory char array that must be null terminated */ struct null_terminated_ptr { + null_terminated_ptr() = default; explicit null_terminated_ptr(char* value) : value(value) {} typename std::add_lvalue_reference::type operator*() const { diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index c60eac758a0..fbd2b996b2d 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -9,6 +9,45 @@ //eos-vm includes #include +// eosio specific specializations +namespace eosio { namespace wasm_backend { + template <> + struct reduce_type { + typedef uint64_t type; + }; + + template + constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && + std::is_same_v>, S> { + return {(uint64_t)val.data.ui}; + } + // we can clean these up if we go with custom vms + template + struct reduce_type> { + typedef uint32_t type; + }; + + template + constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && + std::is_same_v< eosio::chain::array_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + return eosio::chain::array_ptr((typename S::type*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui)); + } + + template <> + struct reduce_type { + typedef uint32_t type; + }; + + template + constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && + std::is_same_v< eosio::chain::null_terminated_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + return eosio::chain::null_terminated_ptr((char*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui)); + } + +}} // ns eosio::wasm_backend + namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { using namespace fc; @@ -20,13 +59,17 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { eos_vm_runtime(); std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; - void immediately_exit_currently_running_module() override; + void immediately_exit_currently_running_module() override { _bkend->immediate_exit(); } private: - backend* _bkend; // non owning pointer to allow for immediate exit + backend* _bkend; // non owning pointer to allow for immediate exit }; +} } } }// eosio::chain::webassembly::wabt_runtime + +#define __EOS_VM_INTRINSIC_NAME(LBL, SUF) LBL##SUF +#define _EOS_VM_INTRINSIC_NAME(LBL, SUF) __INTRINSIC_NAME(LBL, SUF) + #define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ - eosio::wasm_backend::registered_host_functions::add<&CLS::METHOD, eosio::wasm_backend::backend>(#MOD, #NAME); + eosio::wasm_backend::registered_function> _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__){MOD, NAME}; -} } } }// eosio::chain::webassembly::wabt_runtime diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 079983c89e9..ba616952216 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -25,6 +25,8 @@ #include #include +#include + namespace eosio { namespace chain { using namespace webassembly; using namespace webassembly::common; @@ -33,6 +35,8 @@ namespace eosio { namespace chain { wasm_interface::~wasm_interface() {} + wasm_allocator* wasm_interface::get_wasm_allocator() { return wasm_interface_impl::get_wasm_allocator(); } + void wasm_interface::validate(const controller& control, const bytes& code) { Module module; try { @@ -246,19 +250,19 @@ class softfloat_api : public context_aware_api { #pragma GCC diagnostic ignored "-Wstrict-aliasing" // float binops float _eosio_f32_add( float a, float b ) { - float32_t ret = f32_add( to_softfloat32(a), to_softfloat32(b) ); + float32_t ret = ::f32_add( to_softfloat32(a), to_softfloat32(b) ); return *reinterpret_cast(&ret); } float _eosio_f32_sub( float a, float b ) { - float32_t ret = f32_sub( to_softfloat32(a), to_softfloat32(b) ); + float32_t ret = ::f32_sub( to_softfloat32(a), to_softfloat32(b) ); return *reinterpret_cast(&ret); } float _eosio_f32_div( float a, float b ) { - float32_t ret = f32_div( to_softfloat32(a), to_softfloat32(b) ); + float32_t ret = ::f32_div( to_softfloat32(a), to_softfloat32(b) ); return *reinterpret_cast(&ret); } float _eosio_f32_mul( float a, float b ) { - float32_t ret = f32_mul( to_softfloat32(a), to_softfloat32(b) ); + float32_t ret = ::f32_mul( to_softfloat32(a), to_softfloat32(b) ); return *reinterpret_cast(&ret); } #pragma GCC diagnostic pop @@ -274,7 +278,7 @@ class softfloat_api : public context_aware_api { if ( f32_sign_bit(a) != f32_sign_bit(b) ) { return f32_sign_bit(a) ? af : bf; } - return f32_lt(a,b) ? af : bf; + return ::f32_lt(a,b) ? af : bf; } float _eosio_f32_max( float af, float bf ) { float32_t a = to_softfloat32(af); @@ -288,7 +292,7 @@ class softfloat_api : public context_aware_api { if ( f32_sign_bit(a) != f32_sign_bit(b) ) { return f32_sign_bit(a) ? bf : af; } - return f32_lt( a, b ) ? bf : af; + return ::f32_lt( a, b ) ? bf : af; } float _eosio_f32_copysign( float af, float bf ) { float32_t a = to_softfloat32(af); @@ -313,7 +317,7 @@ class softfloat_api : public context_aware_api { return from_softfloat32(a); } float _eosio_f32_sqrt( float a ) { - float32_t ret = f32_sqrt( to_softfloat32(a) ); + float32_t ret = ::f32_sqrt( to_softfloat32(a) ); return from_softfloat32(ret); } // ceil, floor, trunc and nearest are lifted from libc @@ -382,19 +386,19 @@ class softfloat_api : public context_aware_api { if (e >= 0x7f+23) return af; if (s) - y = f32_add( f32_sub( a, float32_t{inv_float_eps} ), float32_t{inv_float_eps} ); + y = ::f32_add( ::f32_sub( a, float32_t{inv_float_eps} ), float32_t{inv_float_eps} ); else - y = f32_sub( f32_add( a, float32_t{inv_float_eps} ), float32_t{inv_float_eps} ); - if (f32_eq( y, {0} ) ) + y = ::f32_sub( ::f32_add( a, float32_t{inv_float_eps} ), float32_t{inv_float_eps} ); + if (::f32_eq( y, {0} ) ) return s ? -0.0f : 0.0f; return from_softfloat32(y); } // float relops - bool _eosio_f32_eq( float a, float b ) { return f32_eq( to_softfloat32(a), to_softfloat32(b) ); } - bool _eosio_f32_ne( float a, float b ) { return !f32_eq( to_softfloat32(a), to_softfloat32(b) ); } - bool _eosio_f32_lt( float a, float b ) { return f32_lt( to_softfloat32(a), to_softfloat32(b) ); } - bool _eosio_f32_le( float a, float b ) { return f32_le( to_softfloat32(a), to_softfloat32(b) ); } + bool _eosio_f32_eq( float a, float b ) { return ::f32_eq( to_softfloat32(a), to_softfloat32(b) ); } + bool _eosio_f32_ne( float a, float b ) { return !::f32_eq( to_softfloat32(a), to_softfloat32(b) ); } + bool _eosio_f32_lt( float a, float b ) { return ::f32_lt( to_softfloat32(a), to_softfloat32(b) ); } + bool _eosio_f32_le( float a, float b ) { return ::f32_le( to_softfloat32(a), to_softfloat32(b) ); } bool _eosio_f32_gt( float af, float bf ) { float32_t a = to_softfloat32(af); float32_t b = to_softfloat32(bf); @@ -402,7 +406,7 @@ class softfloat_api : public context_aware_api { return false; if (is_nan(b)) return false; - return !f32_le( a, b ); + return !::f32_le( a, b ); } bool _eosio_f32_ge( float af, float bf ) { float32_t a = to_softfloat32(af); @@ -411,24 +415,24 @@ class softfloat_api : public context_aware_api { return false; if (is_nan(b)) return false; - return !f32_lt( a, b ); + return !::f32_lt( a, b ); } // double binops double _eosio_f64_add( double a, double b ) { - float64_t ret = f64_add( to_softfloat64(a), to_softfloat64(b) ); + float64_t ret = ::f64_add( to_softfloat64(a), to_softfloat64(b) ); return from_softfloat64(ret); } double _eosio_f64_sub( double a, double b ) { - float64_t ret = f64_sub( to_softfloat64(a), to_softfloat64(b) ); + float64_t ret = ::f64_sub( to_softfloat64(a), to_softfloat64(b) ); return from_softfloat64(ret); } double _eosio_f64_div( double a, double b ) { - float64_t ret = f64_div( to_softfloat64(a), to_softfloat64(b) ); + float64_t ret = ::f64_div( to_softfloat64(a), to_softfloat64(b) ); return from_softfloat64(ret); } double _eosio_f64_mul( double a, double b ) { - float64_t ret = f64_mul( to_softfloat64(a), to_softfloat64(b) ); + float64_t ret = ::f64_mul( to_softfloat64(a), to_softfloat64(b) ); return from_softfloat64(ret); } double _eosio_f64_min( double af, double bf ) { @@ -440,7 +444,7 @@ class softfloat_api : public context_aware_api { return bf; if (f64_sign_bit(a) != f64_sign_bit(b)) return f64_sign_bit(a) ? af : bf; - return f64_lt( a, b ) ? af : bf; + return ::f64_lt( a, b ) ? af : bf; } double _eosio_f64_max( double af, double bf ) { float64_t a = to_softfloat64(af); @@ -451,7 +455,7 @@ class softfloat_api : public context_aware_api { return bf; if (f64_sign_bit(a) != f64_sign_bit(b)) return f64_sign_bit(a) ? bf : af; - return f64_lt( a, b ) ? bf : af; + return ::f64_lt( a, b ) ? bf : af; } double _eosio_f64_copysign( double af, double bf ) { float64_t a = to_softfloat64(af); @@ -477,7 +481,7 @@ class softfloat_api : public context_aware_api { return from_softfloat64(a); } double _eosio_f64_sqrt( double a ) { - float64_t ret = f64_sqrt( to_softfloat64(a) ); + float64_t ret = ::f64_sqrt( to_softfloat64(a) ); return from_softfloat64(ret); } // ceil, floor, trunc and nearest are lifted from libc @@ -486,22 +490,22 @@ class softfloat_api : public context_aware_api { float64_t ret; int e = a.v >> 52 & 0x7ff; float64_t y; - if (e >= 0x3ff+52 || f64_eq( a, { 0 } )) + if (e >= 0x3ff+52 || ::f64_eq( a, { 0 } )) return af; /* y = int(x) - x, where int(x) is an integer neighbor of x */ if (a.v >> 63) - y = f64_sub( f64_add( f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); + y = ::f64_sub( ::f64_add( ::f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); else - y = f64_sub( f64_sub( f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); + y = ::f64_sub( ::f64_sub( ::f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); /* special case because of non-nearest rounding modes */ if (e <= 0x3ff-1) { return a.v >> 63 ? -0.0 : 1.0; //float64_t{0x8000000000000000} : float64_t{0xBE99999A3F800000}; //either -0.0 or 1 } - if (f64_lt( y, to_softfloat64(0) )) { - ret = f64_add( f64_add( a, y ), to_softfloat64(1) ); // 0xBE99999A3F800000 } ); // plus 1 + if (::f64_lt( y, to_softfloat64(0) )) { + ret = ::f64_add( ::f64_add( a, y ), to_softfloat64(1) ); // 0xBE99999A3F800000 } ); // plus 1 return from_softfloat64(ret); } - ret = f64_add( a, y ); + ret = ::f64_add( a, y ); return from_softfloat64(ret); } double _eosio_f64_floor( double af ) { @@ -517,17 +521,17 @@ class softfloat_api : public context_aware_api { return af; } if (a.v >> 63) - y = f64_sub( f64_add( f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); + y = ::f64_sub( ::f64_add( ::f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); else - y = f64_sub( f64_sub( f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); + y = ::f64_sub( ::f64_sub( ::f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ), a ); if (e <= 0x3FF-1) { return a.v>>63 ? -1.0 : 0.0; //float64_t{0xBFF0000000000000} : float64_t{0}; // -1 or 0 } - if ( !f64_le( y, float64_t{0} ) ) { - ret = f64_sub( f64_add(a,y), to_softfloat64(1.0)); + if ( !::f64_le( y, float64_t{0} ) ) { + ret = ::f64_sub( ::f64_add(a,y), to_softfloat64(1.0)); return from_softfloat64(ret); } - ret = f64_add( a, y ); + ret = ::f64_add( a, y ); return from_softfloat64(ret); } double _eosio_f64_trunc( double af ) { @@ -553,19 +557,19 @@ class softfloat_api : public context_aware_api { if ( e >= 0x3FF+52 ) return af; if ( s ) - y = f64_add( f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ); + y = ::f64_add( ::f64_sub( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ); else - y = f64_sub( f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ); - if ( f64_eq( y, float64_t{0} ) ) + y = ::f64_sub( ::f64_add( a, float64_t{inv_double_eps} ), float64_t{inv_double_eps} ); + if ( ::f64_eq( y, float64_t{0} ) ) return s ? -0.0 : 0.0; return from_softfloat64(y); } // double relops - bool _eosio_f64_eq( double a, double b ) { return f64_eq( to_softfloat64(a), to_softfloat64(b) ); } - bool _eosio_f64_ne( double a, double b ) { return !f64_eq( to_softfloat64(a), to_softfloat64(b) ); } - bool _eosio_f64_lt( double a, double b ) { return f64_lt( to_softfloat64(a), to_softfloat64(b) ); } - bool _eosio_f64_le( double a, double b ) { return f64_le( to_softfloat64(a), to_softfloat64(b) ); } + bool _eosio_f64_eq( double a, double b ) { return ::f64_eq( to_softfloat64(a), to_softfloat64(b) ); } + bool _eosio_f64_ne( double a, double b ) { return !::f64_eq( to_softfloat64(a), to_softfloat64(b) ); } + bool _eosio_f64_lt( double a, double b ) { return ::f64_lt( to_softfloat64(a), to_softfloat64(b) ); } + bool _eosio_f64_le( double a, double b ) { return ::f64_le( to_softfloat64(a), to_softfloat64(b) ); } bool _eosio_f64_gt( double af, double bf ) { float64_t a = to_softfloat64(af); float64_t b = to_softfloat64(bf); @@ -573,7 +577,7 @@ class softfloat_api : public context_aware_api { return false; if (is_nan(b)) return false; - return !f64_le( a, b ); + return !::f64_le( a, b ); } bool _eosio_f64_ge( double af, double bf ) { float64_t a = to_softfloat64(af); @@ -582,7 +586,7 @@ class softfloat_api : public context_aware_api { return false; if (is_nan(b)) return false; - return !f64_lt( a, b ); + return !::f64_lt( a, b ); } // float and double conversions @@ -906,8 +910,8 @@ class authorization_api : public context_aware_api { return context.has_authorization( account ); } - void require_authorization(const account_name& account, - const permission_name& permission) { + void require_authorization2(const account_name& account, + const permission_name& permission) { context.require_authorization( account, permission ); } @@ -1846,7 +1850,7 @@ REGISTER_INTRINSICS(action_api, REGISTER_INTRINSICS(authorization_api, (require_recipient, void(int64_t) ) (require_authorization, void(int64_t), "require_auth", void(authorization_api::*)(const account_name&) ) - (require_authorization, void(int64_t, int64_t), "require_auth2", void(authorization_api::*)(const account_name&, const permission_name& permission) ) + (require_authorization2, void(int64_t, int64_t), "require_auth2", void(authorization_api::*)(const account_name&, const permission_name& permission) ) (has_authorization, int(int64_t), "has_auth", bool(authorization_api::*)(const account_name&)const ) (is_account, int(int64_t) ) ); diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 04882cb6fb3..176a1383fa5 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -11,7 +11,7 @@ using namespace eosio::wasm_backend; namespace wasm_constraints = eosio::chain::wasm_constraints; -using backend_t = backend>; +using backend_t = backend; class eos_vm_instantiated_module : public wasm_instantiated_module_interface { public: @@ -20,15 +20,15 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { - _instantiated_module->set_wasm_allocator( context.get_wasm_allocator() ); - if (!(const auto& res = _instantiated_module->run_start())) - EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - - if (!(const auto& res = _instantiated_module(context.get_registered_host_functions(), "env", "apply", - (uint64_t)context.get_receiver(), - (uint64_t)context.get_action().account(), - (uint64_t)context.get_action().name)) - EOS_ASSERT(false, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); + _instantiated_module->set_wasm_allocator( wasm_interface::get_wasm_allocator() ); + //if (!(const auto& res = _instantiated_module->run_start())) + // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); + + const auto& res = _instantiated_module->call(&context, "env", "apply", + (uint64_t)context.get_receiver(), + (uint64_t)context.get_action().account, + (uint64_t)context.get_action().name); + //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); } private: @@ -39,13 +39,10 @@ eos_vm_runtime::eos_vm_runtime() {} std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); - std::unique_ptr bkend = std::make_unique( cb ); + std::unique_ptr bkend = std::make_unique( cb ); + registered_host_functions::resolve(bkend->get_module()); _bkend = bkend.get(); - return std::make_unique(std::move(bkend)); -} - -void eos_vm_runtime::immediately_exit_currently_running_module() { - _bkend->immediate_exit(); + return std::make_unique(std::move(bkend)); } }}}} diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index cda857ff2c2..83140682834 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -387,6 +387,8 @@ namespace eosio { namespace testing { vcfg.wasm_runtime = chain::wasm_interface::vm_type::wavm; else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wabt")) vcfg.wasm_runtime = chain::wasm_interface::vm_type::wabt; + else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm")) + vcfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm; } return vcfg; } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 4c0ef6acdb0..27bc99aad9f 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -213,7 +213,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip ("protocol-features-dir", bpo::value()->default_value("protocol_features"), "the location of the protocol_features directory (absolute path or relative to application config dir)") ("checkpoint", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") - ("wasm-runtime", bpo::value()->value_name("wavm/wabt"), "Override default WASM runtime") + ("wasm-runtime", bpo::value()->value_name("wavm/wabt/eos-vm"), "Override default WASM runtime") ("abi-serializer-max-time-ms", bpo::value()->default_value(config::default_abi_serializer_max_time_ms), "Override default maximum ABI serialization time allowed in ms") ("chain-state-db-size-mb", bpo::value()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MiB) of the chain state database") From d62678921ed624027b3ef01a099df2e457675923 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Apr 2019 13:13:27 -0400 Subject: [PATCH 03/88] solved issue with linking --- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index fbd2b996b2d..01a3ea19483 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -70,6 +70,7 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { #define __EOS_VM_INTRINSIC_NAME(LBL, SUF) LBL##SUF #define _EOS_VM_INTRINSIC_NAME(LBL, SUF) __INTRINSIC_NAME(LBL, SUF) -#define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\ - eosio::wasm_backend::registered_function> _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__){MOD, NAME}; +#define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ + eosio::wasm_backend::registered_function> _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); + From cc0092bc948d2fab18e5c26f257e6f598d0bce5e Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Apr 2019 19:28:32 -0400 Subject: [PATCH 04/88] new problems --- .../eosio/chain/webassembly/eos-vm.hpp | 33 ++++++++++++++----- libraries/chain/webassembly/eos-vm.cpp | 10 ++++-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 01a3ea19483..f6d12ca7406 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -16,8 +16,8 @@ namespace eosio { namespace wasm_backend { typedef uint64_t type; }; - template - constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && + template + constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups&, Backend& backend, T&& val) -> std::enable_if_t && std::is_same_v>, S> { return {(uint64_t)val.data.ui}; } @@ -27,11 +27,26 @@ namespace eosio { namespace wasm_backend { typedef uint32_t type; }; - template - constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && + template + constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups& cleanups, Backend& backend, T&& val) -> std::enable_if_t && std::is_same_v< eosio::chain::array_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - return eosio::chain::array_ptr((typename S::type*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui)); + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + size_t i = std::tuple_size::value-1; + auto* ptr = (*((std::remove_reference_t*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui))); + if constexpr (std::tuple_size::value > I) { + const auto& len = std::get::type>>(op.get_back(i-I)); + if ((uintptr_t)ptr % alignof(S) != 0) { + align_ptr_triple apt; + apt.s = sizeof(S)*len; + std::vector> cpy(len > 0 ? len : 1); + apt.o = ptr; + S* ptr = &cpy[0]; + apt.n = ptr; + memcpy(apt.n, apt.o, apt.s); + cleanups.emplace_back(std::move(apt)); + } + } + return eosio::chain::array_ptr(ptr); } template <> @@ -39,9 +54,9 @@ namespace eosio { namespace wasm_backend { typedef uint32_t type; }; - template - constexpr auto get_value(Backend& backend, T&& val) -> std::enable_if_t && - std::is_same_v< eosio::chain::null_terminated_ptr, S> && + template + constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups&, Backend& backend, T&& val) -> std::enable_if_t && + std::is_same_v< eosio::chain::null_terminated_ptr, S> && !std::is_lvalue_reference_v && !std::is_pointer_v, S> { return eosio::chain::null_terminated_ptr((char*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui)); } diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 176a1383fa5..8693aaf4931 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -1,7 +1,7 @@ #include #include #include - +#include //eos-vm includes #include @@ -38,7 +38,13 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { eos_vm_runtime::eos_vm_runtime() {} std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { - std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); + //std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); + std::vector cb; + cb.resize(code_size); + memcpy(cb.data(), code_bytes, code_size); + std::ofstream mf("temp.wasm"); + mf.write((char*)cb.data(), cb.size()); + mf.close(); std::unique_ptr bkend = std::make_unique( cb ); registered_host_functions::resolve(bkend->get_module()); _bkend = bkend.get(); From c6dd8c8094925fab689756e0c0fcac369c2d14dd Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Apr 2019 20:41:28 -0400 Subject: [PATCH 05/88] fixed issue --- .../include/eosio/chain/webassembly/eos-vm.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index f6d12ca7406..e195f9822bc 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -32,16 +32,17 @@ namespace eosio { namespace wasm_backend { std::is_same_v< eosio::chain::array_ptr, S> && !std::is_lvalue_reference_v && !std::is_pointer_v, S> { size_t i = std::tuple_size::value-1; - auto* ptr = (*((std::remove_reference_t*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui))); + using ptr_ty = typename S::type; + auto* ptr = (ptr_ty*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui); if constexpr (std::tuple_size::value > I) { - const auto& len = std::get::type>>(op.get_back(i-I)); + const auto& len = std::get::type>>(op.get_back(i-I)).data.ui; if ((uintptr_t)ptr % alignof(S) != 0) { align_ptr_triple apt; apt.s = sizeof(S)*len; - std::vector> cpy(len > 0 ? len : 1); - apt.o = ptr; - S* ptr = &cpy[0]; - apt.n = ptr; + std::vector::type> cpy(len > 0 ? len : 1); + apt.o = (void*)ptr; + ptr = &cpy[0]; + apt.n = (void*)ptr; memcpy(apt.n, apt.o, apt.s); cleanups.emplace_back(std::move(apt)); } From 1d3f79fada3d6aedabf4e52cb6d49d14c89a79ec Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 30 Apr 2019 02:39:33 -0400 Subject: [PATCH 06/88] still working on it --- .../include/eosio/chain/webassembly/wabt.hpp | 25 +++++++++++++++++++ .../include/eosio/chain/webassembly/wavm.hpp | 25 +++++++++++++++++++ libraries/chain/wasm_interface.cpp | 1 + libraries/chain/webassembly/eos-vm.cpp | 3 ++- 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 31456dc1dda..040505b014b 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -686,23 +686,48 @@ struct intrinsic_function_invoker { template struct intrinsic_function_invoker_wrapper; +template +struct void_ret_wrapper { + using type = T; +}; + +template<> +struct void_ret_wrapper { + using type = char; +}; + +template +using void_ret_wrapper_t = typename void_ret_wrapper::type; + template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 5bce9db8b40..04c36e8f153 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -675,26 +675,51 @@ struct intrinsic_function_invoker { } }; +template +struct void_ret_wrapper { + using type = T; +}; + +template<> +struct void_ret_wrapper { + using type = char; +}; + +template +using void_ret_wrapper_t = typename void_ret_wrapper::type; + template struct intrinsic_function_invoker_wrapper; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { + static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index ba616952216..3b7fda46a5a 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1010,6 +1010,7 @@ class action_api : public context_aware_api { } int action_data_size() { + std::cout << "ADS " << context.get_action().data.size() << "\n"; return context.get_action().data.size(); } diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 8693aaf4931..1b2deb1bff5 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -23,7 +23,8 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module->set_wasm_allocator( wasm_interface::get_wasm_allocator() ); //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - + + std::cout << "CTX " << &context << "\n"; const auto& res = _instantiated_module->call(&context, "env", "apply", (uint64_t)context.get_receiver(), (uint64_t)context.get_action().account, From cbc2a28dde16d0afa6edcd58316eb66ad120b60f Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 6 May 2019 02:48:49 -0400 Subject: [PATCH 07/88] close to ready for replay --- libraries/CMakeLists.txt | 6 +++- .../eosio/chain/webassembly/eos-vm.hpp | 35 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 292e7021f46..9727f511f19 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -14,7 +14,11 @@ set(RUN_RE2C OFF CACHE BOOL "Run re2c") set(WITH_EXCEPTIONS ON CACHE BOOL "Build with exceptions enabled" FORCE) add_subdirectory( wabt ) -set(USE_EXISTING_SOFTFLOAT 1) +set(USE_EXISTING_SOFTFLOAT ON) +set(ENABLE_TESTS OFF) +set(ENABLE_ADDRESS_SANITIZER OFF) +set(ENABLE_UNDEFINED_BEHAVIOR_SANITIZER OFF) +set(ENABLE_PROFILE OFF) add_subdirectory( eos-vm ) set(ENABLE_STATIC ON) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index e195f9822bc..d64f36aa741 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -16,24 +16,26 @@ namespace eosio { namespace wasm_backend { typedef uint64_t type; }; - template - constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups&, Backend& backend, T&& val) -> std::enable_if_t && - std::is_same_v>, S> { + template + constexpr auto get_value(operand_stack& op, Cleanups&, WAlloc*, T&& val) + -> std::enable_if_t && std::is_same_v>, S> { return {(uint64_t)val.data.ui}; } + // we can clean these up if we go with custom vms template struct reduce_type> { typedef uint32_t type; }; - template - constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups& cleanups, Backend& backend, T&& val) -> std::enable_if_t && - std::is_same_v< eosio::chain::array_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + template + constexpr auto get_value(operand_stack& op, Cleanups& cleanups, WAlloc* walloc, T&& val) + -> std::enable_if_t && + std::is_same_v< eosio::chain::array_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { size_t i = std::tuple_size::value-1; using ptr_ty = typename S::type; - auto* ptr = (ptr_ty*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui); + auto* ptr = (ptr_ty*)(walloc->template get_base_ptr()+val.data.ui); if constexpr (std::tuple_size::value > I) { const auto& len = std::get::type>>(op.get_back(i-I)).data.ui; if ((uintptr_t)ptr % alignof(S) != 0) { @@ -41,9 +43,9 @@ namespace eosio { namespace wasm_backend { apt.s = sizeof(S)*len; std::vector::type> cpy(len > 0 ? len : 1); apt.o = (void*)ptr; - ptr = &cpy[0]; + ptr = &cpy; apt.n = (void*)ptr; - memcpy(apt.n, apt.o, apt.s); + bytes_copy(apt.n, apt.o, apt.s); cleanups.emplace_back(std::move(apt)); } } @@ -55,11 +57,12 @@ namespace eosio { namespace wasm_backend { typedef uint32_t type; }; - template - constexpr auto get_value(eosio::wasm_backend::operand_stack& op, Cleanups&, Backend& backend, T&& val) -> std::enable_if_t && - std::is_same_v< eosio::chain::null_terminated_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - return eosio::chain::null_terminated_ptr((char*)(backend.get_wasm_allocator()->template get_base_ptr()+val.data.ui)); + template + constexpr auto get_value(operand_stack& op, Cleanups&, WAlloc* walloc, T&& val) + -> std::enable_if_t && + std::is_same_v< eosio::chain::null_terminated_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + return eosio::chain::null_terminated_ptr((char*)(walloc->template get_base_ptr()+val.data.ui)); } }} // ns eosio::wasm_backend @@ -87,6 +90,6 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { #define _EOS_VM_INTRINSIC_NAME(LBL, SUF) __INTRINSIC_NAME(LBL, SUF) #define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ - eosio::wasm_backend::registered_function> _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); + eosio::wasm_backend::registered_function _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); From 59c904c94e2dc5e9a59ba74c162025728de35a61 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 6 May 2019 03:57:06 -0400 Subject: [PATCH 08/88] need to change a bit for alignment --- .../include/eosio/chain/webassembly/eos-vm.hpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index d64f36aa741..4c158284c43 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -37,13 +37,19 @@ namespace eosio { namespace wasm_backend { using ptr_ty = typename S::type; auto* ptr = (ptr_ty*)(walloc->template get_base_ptr()+val.data.ui); if constexpr (std::tuple_size::value > I) { - const auto& len = std::get::type>>(op.get_back(i-I)).data.ui; + const auto& next_arg = std::get::type>>(op.get_back(i-I)).data.ui; + if constexpr ( (std::tuple_size::value > I+1) + && std::is_same_v>) { + sz = std::get::type>>(op.get_back(i-(I+1))).data.ui; + } else { + sz = next_arg; + } if ((uintptr_t)ptr % alignof(S) != 0) { align_ptr_triple apt; - apt.s = sizeof(S)*len; - std::vector::type> cpy(len > 0 ? len : 1); + apt.s = sizeof(S)*sz; + std::vector::type> cpy(sz > 0 ? sz : 1); apt.o = (void*)ptr; - ptr = &cpy; + ptr = (decltype(ptr))cpy.data(); apt.n = (void*)ptr; bytes_copy(apt.n, apt.o, apt.s); cleanups.emplace_back(std::move(apt)); From 60c4a014fe884b1d760babf02c1e70f5e158799c Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 6 May 2019 19:34:24 -0400 Subject: [PATCH 09/88] working on a solution --- CMakeLists.txt | 16 ++++++++- .../eosio/chain/wasm_interface_private.hpp | 4 +-- .../eosio/chain/webassembly/common.hpp | 10 +++--- .../eosio/chain/webassembly/eos-vm.hpp | 36 +++++++++++-------- libraries/chain/wasm_interface.cpp | 12 +++++-- libraries/chain/webassembly/eos-vm.cpp | 10 +++--- 6 files changed, 57 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d708ac5d44..7dfb68f1f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,18 @@ else() set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") endif() +find_program(CCACHE_PROGRAM ccache) +find_program(SCCACHE_PROGRAM ccache) +if (SCCACHE_PROGRAM) + message(STATUS "EOSIO found sccache") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${SCCACHE_PROGRAM}") + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${SCCACHE_PROGRAM}") +elseif (CCACHE_PROGRAM) + message(STATUS "EOSIO found ccache") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}") +endif() + set( CLI_CLIENT_EXECUTABLE_NAME cleos ) set( NODE_EXECUTABLE_NAME nodeos ) set( KEY_STORE_EXECUTABLE_NAME keosd ) @@ -63,6 +75,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID} add_compile_options(-fcolor-diagnostics) endif() endif() + add_compile_options(-fno-strict-aliasing) set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") set(BUILD_DOXYGEN FALSE CACHE BOOL "Build doxygen documentation on every make") @@ -157,7 +170,8 @@ else( WIN32 ) # Apple AND Linux if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring EOSIO on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall -Wno-deprecated-declarations" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall -Wno-deprecated-declarations -fsanitize=undefined -fno-omit-frame-pointer" ) + set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring EOSIO on Linux" ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index cf8df0afa9e..85a3c7f34ae 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -62,8 +62,8 @@ namespace eosio { namespace chain { } static wasm_allocator* get_wasm_allocator() { - thread_local wasm_allocator walloc; - return &walloc; + static wasm_allocator walloc; + return &walloc; } std::vector parse_initial_memory(const Module& module) { diff --git a/libraries/chain/include/eosio/chain/webassembly/common.hpp b/libraries/chain/include/eosio/chain/webassembly/common.hpp index 542d3c5b37e..e12017437fd 100644 --- a/libraries/chain/include/eosio/chain/webassembly/common.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/common.hpp @@ -70,9 +70,8 @@ namespace eosio { namespace chain { return value; } - template - operator U *() const { - return static_cast(value); + operator T *() const { + return value; } T *value; @@ -93,9 +92,8 @@ namespace eosio { namespace chain { return value; } - template - operator U *() const { - return static_cast(value); + operator char *() const { + return value; } char *value; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 4c158284c43..d17db22a9f0 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -32,30 +32,40 @@ namespace eosio { namespace wasm_backend { constexpr auto get_value(operand_stack& op, Cleanups& cleanups, WAlloc* walloc, T&& val) -> std::enable_if_t && std::is_same_v< eosio::chain::array_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - size_t i = std::tuple_size::value-1; + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + //size_t i = std::tuple_size::value-1; using ptr_ty = typename S::type; - auto* ptr = (ptr_ty*)(walloc->template get_base_ptr()+val.data.ui); + using under_ty = std::remove_pointer_t; + std::cout << (int*)walloc->template get_base_ptr() << "val.data " << val.data.ui << "\n"; + auto* ptr = (ptr_ty*)((walloc->template get_base_ptr())+val.data.ui); + /* if constexpr (std::tuple_size::value > I) { const auto& next_arg = std::get::type>>(op.get_back(i-I)).data.ui; - if constexpr ( (std::tuple_size::value > I+1) - && std::is_same_v>) { + size_t sz = 0; + if constexpr ( (std::tuple_size::value > I+1) + && std::is_same_v>) { sz = std::get::type>>(op.get_back(i-(I+1))).data.ui; + std::cout << "sz0 " << sz << "\n"; } else { sz = next_arg; - } - if ((uintptr_t)ptr % alignof(S) != 0) { + std::cout << "sz1 " << sz << " sizeof " << sizeof(under_ty) << " alignof " << alignof(under_ty) << "\n"; + } + if ((uintptr_t)ptr % 16 != 0) { + std::cout << "aligning\n"; align_ptr_triple apt; - apt.s = sizeof(S)*sz; - std::vector::type> cpy(sz > 0 ? sz : 1); + apt.s = sizeof(under_ty)*sz; + std::vector> cpy(sz > 0 ? sz : 1); apt.o = (void*)ptr; ptr = (decltype(ptr))cpy.data(); apt.n = (void*)ptr; - bytes_copy(apt.n, apt.o, apt.s); - cleanups.emplace_back(std::move(apt)); + //memcpy((std::remove_const_t*)apt.n, (const under_ty*)apt.o, apt.s); + memmove( static_cast(apt.n), static_cast(apt.o), apt.s); + if constexpr (!std::is_const_v) + cleanups.emplace_back(std::move(apt)); } } - return eosio::chain::array_ptr(ptr); + */ + return eosio::chain::array_ptr(ptr); } template <> @@ -97,5 +107,3 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { #define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ eosio::wasm_backend::registered_function _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); - - diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 3b7fda46a5a..1629334e113 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -31,7 +31,9 @@ namespace eosio { namespace chain { using namespace webassembly; using namespace webassembly::common; - wasm_interface::wasm_interface(vm_type vm, const chainbase::database& d) : my( new wasm_interface_impl(vm, d) ) {} + wasm_interface::wasm_interface(vm_type vm, const chainbase::database& d) : my( new wasm_interface_impl(vm, d) ) { + (void)get_wasm_allocator(); + } wasm_interface::~wasm_interface() {} @@ -1004,13 +1006,15 @@ class action_api : public context_aware_api { if( buffer_size == 0 ) return s; auto copy_size = std::min( buffer_size, s ); - memcpy( memory, context.get_action().data.data(), copy_size ); + const char* d = context.get_action().data.data(); + memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); return copy_size; } int action_data_size() { - std::cout << "ADS " << context.get_action().data.size() << "\n"; + std::cout << "THIS " << this << "\n"; + std::cout << "ADS " << context.get_action().data.size() << "\n"; return context.get_action().data.size(); } @@ -1965,6 +1969,8 @@ std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) { runtime = eosio::chain::wasm_interface::vm_type::wavm; else if (s == "wabt") runtime = eosio::chain::wasm_interface::vm_type::wabt; + else if (s == "eos-vm") + runtime = eosio::chain::wasm_interface::vm_type::eos_vm; else in.setstate(std::ios_base::failbit); return in; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 1b2deb1bff5..81b5c40dad8 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -24,7 +24,7 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - std::cout << "CTX " << &context << "\n"; + std::cout << "CTX " << &context << "\n"; const auto& res = _instantiated_module->call(&context, "env", "apply", (uint64_t)context.get_receiver(), (uint64_t)context.get_action().account, @@ -39,10 +39,10 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { eos_vm_runtime::eos_vm_runtime() {} std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { - //std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); - std::vector cb; - cb.resize(code_size); - memcpy(cb.data(), code_bytes, code_size); + std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); + //std::vector cb; + //cb.resize(code_size); + //memcpy(cb.data(), code_bytes, code_size); std::ofstream mf("temp.wasm"); mf.write((char*)cb.data(), cb.size()); mf.close(); From f4ba2d8848b9e6648357a517315748a92ff46c73 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 7 May 2019 01:51:23 -0400 Subject: [PATCH 10/88] still sse3 issues --- .../chain/include/eosio/chain/webassembly/eos-vm.hpp | 10 ++++++++++ libraries/chain/wasm_interface.cpp | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index d17db22a9f0..a6a1407e5a4 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -68,6 +68,16 @@ namespace eosio { namespace wasm_backend { return eosio::chain::array_ptr(ptr); } + template + struct construct_derived { + static auto &value(Ctx& ctx) { return ctx.trx_context; } + }; + + template <> + struct construct_derived { + static auto &value(eosio::chain::apply_context& ctx) { return ctx; } + }; + template <> struct reduce_type { typedef uint32_t type; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 1629334e113..376b4beec06 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1001,13 +1001,15 @@ class action_api : public context_aware_api { action_api( apply_context& ctx ) :context_aware_api(ctx,true){} - int read_action_data(array_ptr memory, size_t buffer_size) { + int read_action_data(array_ptr memory, size_t buffer_size) __attribute__((target("no-sse"))) { auto s = context.get_action().data.size(); if( buffer_size == 0 ) return s; auto copy_size = std::min( buffer_size, s ); const char* d = context.get_action().data.data(); - memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); + for (int i=0; i < copy_size; i++) + ((char*)memory.value)[i] = (context.get_action().data.data())[i]; + //memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); return copy_size; } From 46c884ec32d8171e97b24b0692e1925ea8ff0c20 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 7 May 2019 19:24:51 -0400 Subject: [PATCH 11/88] Fixes for eosvm integration --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dfb68f1f73..9fb920049c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,6 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID} add_compile_options(-fcolor-diagnostics) endif() endif() - add_compile_options(-fno-strict-aliasing) set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") set(BUILD_DOXYGEN FALSE CACHE BOOL "Build doxygen documentation on every make") @@ -170,8 +169,8 @@ else( WIN32 ) # Apple AND Linux if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring EOSIO on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall -Wno-deprecated-declarations -fsanitize=undefined -fno-omit-frame-pointer" ) - set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall -Wno-deprecated-declarations" ) + set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring EOSIO on Linux" ) From b7641fde73ded3b18d6462aab49c7936b45d2ddb Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 9 May 2019 03:05:15 -0400 Subject: [PATCH 12/88] more fixes --- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index a6a1407e5a4..e9f5e05fe49 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -36,7 +36,6 @@ namespace eosio { namespace wasm_backend { //size_t i = std::tuple_size::value-1; using ptr_ty = typename S::type; using under_ty = std::remove_pointer_t; - std::cout << (int*)walloc->template get_base_ptr() << "val.data " << val.data.ui << "\n"; auto* ptr = (ptr_ty*)((walloc->template get_base_ptr())+val.data.ui); /* if constexpr (std::tuple_size::value > I) { From 5a9bf2c68e3ffd95587f1d04e47f64c32cd50f65 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 9 May 2019 18:43:14 -0400 Subject: [PATCH 13/88] further fixes for eos-vm integration --- .../eosio/chain/webassembly/eos-vm.hpp | 58 ++---- libraries/chain/wasm_interface.cpp | 196 +++++++++--------- libraries/chain/webassembly/eos-vm.cpp | 1 - 3 files changed, 111 insertions(+), 144 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index e9f5e05fe49..539448ad138 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -16,9 +16,9 @@ namespace eosio { namespace wasm_backend { typedef uint64_t type; }; - template - constexpr auto get_value(operand_stack& op, Cleanups&, WAlloc*, T&& val) - -> std::enable_if_t && std::is_same_v>, S> { + template + constexpr auto get_value(WAlloc*, T&& val) + -> std::enable_if_t && std::is_same_v>, S> { return {(uint64_t)val.data.ui}; } @@ -28,43 +28,13 @@ namespace eosio { namespace wasm_backend { typedef uint32_t type; }; - template - constexpr auto get_value(operand_stack& op, Cleanups& cleanups, WAlloc* walloc, T&& val) - -> std::enable_if_t && - std::is_same_v< eosio::chain::array_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - //size_t i = std::tuple_size::value-1; + template + constexpr auto get_value(WAlloc* walloc, T&& val) + -> std::enable_if_t && + std::is_same_v< eosio::chain::array_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { using ptr_ty = typename S::type; - using under_ty = std::remove_pointer_t; - auto* ptr = (ptr_ty*)((walloc->template get_base_ptr())+val.data.ui); - /* - if constexpr (std::tuple_size::value > I) { - const auto& next_arg = std::get::type>>(op.get_back(i-I)).data.ui; - size_t sz = 0; - if constexpr ( (std::tuple_size::value > I+1) - && std::is_same_v>) { - sz = std::get::type>>(op.get_back(i-(I+1))).data.ui; - std::cout << "sz0 " << sz << "\n"; - } else { - sz = next_arg; - std::cout << "sz1 " << sz << " sizeof " << sizeof(under_ty) << " alignof " << alignof(under_ty) << "\n"; - } - if ((uintptr_t)ptr % 16 != 0) { - std::cout << "aligning\n"; - align_ptr_triple apt; - apt.s = sizeof(under_ty)*sz; - std::vector> cpy(sz > 0 ? sz : 1); - apt.o = (void*)ptr; - ptr = (decltype(ptr))cpy.data(); - apt.n = (void*)ptr; - //memcpy((std::remove_const_t*)apt.n, (const under_ty*)apt.o, apt.s); - memmove( static_cast(apt.n), static_cast(apt.o), apt.s); - if constexpr (!std::is_const_v) - cleanups.emplace_back(std::move(apt)); - } - } - */ - return eosio::chain::array_ptr(ptr); + return eosio::chain::array_ptr((ptr_ty*)((walloc->template get_base_ptr())+val.data.ui)); } template @@ -82,11 +52,11 @@ namespace eosio { namespace wasm_backend { typedef uint32_t type; }; - template - constexpr auto get_value(operand_stack& op, Cleanups&, WAlloc* walloc, T&& val) - -> std::enable_if_t && - std::is_same_v< eosio::chain::null_terminated_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { + template + constexpr auto get_value(WAlloc* walloc, T&& val) + -> std::enable_if_t && + std::is_same_v< eosio::chain::null_terminated_ptr, S> && + !std::is_lvalue_reference_v && !std::is_pointer_v, S> { return eosio::chain::null_terminated_ptr((char*)(walloc->template get_base_ptr()+val.data.ui)); } diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 376b4beec06..8bc9c327919 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -904,16 +904,16 @@ class authorization_api : public context_aware_api { public: using context_aware_api::context_aware_api; - void require_authorization( const account_name& account ) { + void require_auth( account_name account ) { context.require_authorization( account ); } - bool has_authorization( const account_name& account )const { + bool has_auth( account_name account )const { return context.has_authorization( account ); } - void require_authorization2(const account_name& account, - const permission_name& permission) { + void require_auth2( account_name account, + permission_name permission) { context.require_authorization( account, permission ); } @@ -921,7 +921,7 @@ class authorization_api : public context_aware_api { context.require_recipient( recipient ); } - bool is_account( const account_name& account )const { + bool is_account( account_name account )const { return context.is_account( account ); } @@ -1015,8 +1015,6 @@ class action_api : public context_aware_api { } int action_data_size() { - std::cout << "THIS " << this << "\n"; - std::cout << "ADS " << context.get_action().data.size() << "\n"; return context.get_action().data.size(); } @@ -1698,53 +1696,53 @@ class call_depth_api : public context_aware_api { }; REGISTER_INJECTED_INTRINSICS(call_depth_api, - (call_depth_assert, void() ) + (call_depth_assert, void() ) ); REGISTER_INTRINSICS(compiler_builtins, - (__ashlti3, void(int, int64_t, int64_t, int) ) - (__ashrti3, void(int, int64_t, int64_t, int) ) - (__lshlti3, void(int, int64_t, int64_t, int) ) - (__lshrti3, void(int, int64_t, int64_t, int) ) - (__divti3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__udivti3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__modti3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__umodti3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__multi3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__addtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__subtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__multf3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__divtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) - (__eqtf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__netf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__getf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__gttf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__lttf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__letf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__cmptf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__unordtf2, int(int64_t, int64_t, int64_t, int64_t) ) - (__negtf2, void (int, int64_t, int64_t) ) - (__floatsitf, void (int, int) ) - (__floatunsitf, void (int, int) ) - (__floatditf, void (int, int64_t) ) - (__floatunditf, void (int, int64_t) ) - (__floattidf, double (int64_t, int64_t) ) - (__floatuntidf, double (int64_t, int64_t) ) - (__floatsidf, double(int) ) - (__extendsftf2, void(int, float) ) - (__extenddftf2, void(int, double) ) - (__fixtfti, void(int, int64_t, int64_t) ) - (__fixtfdi, int64_t(int64_t, int64_t) ) - (__fixtfsi, int(int64_t, int64_t) ) - (__fixunstfti, void(int, int64_t, int64_t) ) - (__fixunstfdi, int64_t(int64_t, int64_t) ) - (__fixunstfsi, int(int64_t, int64_t) ) - (__fixsfti, void(int, float) ) - (__fixdfti, void(int, double) ) - (__fixunssfti, void(int, float) ) - (__fixunsdfti, void(int, double) ) - (__trunctfdf2, double(int64_t, int64_t) ) - (__trunctfsf2, float(int64_t, int64_t) ) + (__ashlti3, void(int, int64_t, int64_t, int) ) + (__ashrti3, void(int, int64_t, int64_t, int) ) + (__lshlti3, void(int, int64_t, int64_t, int) ) + (__lshrti3, void(int, int64_t, int64_t, int) ) + (__divti3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__udivti3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__modti3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__umodti3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__multi3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__addtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__subtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__multf3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__divtf3, void(int, int64_t, int64_t, int64_t, int64_t) ) + (__eqtf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__netf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__getf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__gttf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__lttf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__letf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__cmptf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__unordtf2, int(int64_t, int64_t, int64_t, int64_t) ) + (__negtf2, void (int, int64_t, int64_t) ) + (__floatsitf, void (int, int) ) + (__floatunsitf, void (int, int) ) + (__floatditf, void (int, int64_t) ) + (__floatunditf, void (int, int64_t) ) + (__floattidf, double (int64_t, int64_t) ) + (__floatuntidf, double (int64_t, int64_t) ) + (__floatsidf, double(int) ) + (__extendsftf2, void(int, float) ) + (__extenddftf2, void(int, double) ) + (__fixtfti, void(int, int64_t, int64_t) ) + (__fixtfdi, int64_t(int64_t, int64_t) ) + (__fixtfsi, int(int64_t, int64_t) ) + (__fixunstfti, void(int, int64_t, int64_t) ) + (__fixunstfdi, int64_t(int64_t, int64_t) ) + (__fixunstfsi, int(int64_t, int64_t) ) + (__fixsfti, void(int, float) ) + (__fixdfti, void(int, double) ) + (__fixunssfti, void(int, float) ) + (__fixunsdfti, void(int, double) ) + (__trunctfdf2, double(int64_t, int64_t) ) + (__trunctfsf2, float(int64_t, int64_t) ) ); REGISTER_INTRINSICS(privileged_api, @@ -1761,7 +1759,7 @@ REGISTER_INTRINSICS(privileged_api, ); REGISTER_INJECTED_INTRINSICS(transaction_context, - (checktime, void()) + (checktime, void() ) ); REGISTER_INTRINSICS(producer_api, @@ -1769,40 +1767,40 @@ REGISTER_INTRINSICS(producer_api, ); #define DB_SECONDARY_INDEX_METHODS_SIMPLE(IDX) \ - (db_##IDX##_store, int(int64_t,int64_t,int64_t,int64_t,int))\ - (db_##IDX##_remove, void(int))\ - (db_##IDX##_update, void(int,int64_t,int))\ - (db_##IDX##_find_primary, int(int64_t,int64_t,int64_t,int,int64_t))\ - (db_##IDX##_find_secondary, int(int64_t,int64_t,int64_t,int,int))\ - (db_##IDX##_lowerbound, int(int64_t,int64_t,int64_t,int,int))\ - (db_##IDX##_upperbound, int(int64_t,int64_t,int64_t,int,int))\ - (db_##IDX##_end, int(int64_t,int64_t,int64_t))\ - (db_##IDX##_next, int(int, int))\ - (db_##IDX##_previous, int(int, int)) + (db_##IDX##_store, int(int64_t,int64_t,int64_t,int64_t,int) )\ + (db_##IDX##_remove, void(int) )\ + (db_##IDX##_update, void(int,int64_t,int) )\ + (db_##IDX##_find_primary, int(int64_t,int64_t,int64_t,int,int64_t) )\ + (db_##IDX##_find_secondary, int(int64_t,int64_t,int64_t,int,int) )\ + (db_##IDX##_lowerbound, int(int64_t,int64_t,int64_t,int,int) )\ + (db_##IDX##_upperbound, int(int64_t,int64_t,int64_t,int,int) )\ + (db_##IDX##_end, int(int64_t,int64_t,int64_t) )\ + (db_##IDX##_next, int(int, int) )\ + (db_##IDX##_previous, int(int, int) ) #define DB_SECONDARY_INDEX_METHODS_ARRAY(IDX) \ - (db_##IDX##_store, int(int64_t,int64_t,int64_t,int64_t,int,int))\ - (db_##IDX##_remove, void(int))\ - (db_##IDX##_update, void(int,int64_t,int,int))\ - (db_##IDX##_find_primary, int(int64_t,int64_t,int64_t,int,int,int64_t))\ - (db_##IDX##_find_secondary, int(int64_t,int64_t,int64_t,int,int,int))\ - (db_##IDX##_lowerbound, int(int64_t,int64_t,int64_t,int,int,int))\ - (db_##IDX##_upperbound, int(int64_t,int64_t,int64_t,int,int,int))\ - (db_##IDX##_end, int(int64_t,int64_t,int64_t))\ - (db_##IDX##_next, int(int, int))\ - (db_##IDX##_previous, int(int, int)) + (db_##IDX##_store, int(int64_t,int64_t,int64_t,int64_t,int,int) )\ + (db_##IDX##_remove, void(int) )\ + (db_##IDX##_update, void(int,int64_t,int,int) )\ + (db_##IDX##_find_primary, int(int64_t,int64_t,int64_t,int,int,int64_t) )\ + (db_##IDX##_find_secondary, int(int64_t,int64_t,int64_t,int,int,int) )\ + (db_##IDX##_lowerbound, int(int64_t,int64_t,int64_t,int,int,int) )\ + (db_##IDX##_upperbound, int(int64_t,int64_t,int64_t,int,int,int) )\ + (db_##IDX##_end, int(int64_t,int64_t,int64_t) )\ + (db_##IDX##_next, int(int, int) )\ + (db_##IDX##_previous, int(int, int) ) REGISTER_INTRINSICS( database_api, - (db_store_i64, int(int64_t,int64_t,int64_t,int64_t,int,int)) - (db_update_i64, void(int,int64_t,int,int)) - (db_remove_i64, void(int)) - (db_get_i64, int(int, int, int)) - (db_next_i64, int(int, int)) - (db_previous_i64, int(int, int)) - (db_find_i64, int(int64_t,int64_t,int64_t,int64_t)) - (db_lowerbound_i64, int(int64_t,int64_t,int64_t,int64_t)) - (db_upperbound_i64, int(int64_t,int64_t,int64_t,int64_t)) - (db_end_i64, int(int64_t,int64_t,int64_t)) + (db_store_i64, int(int64_t,int64_t,int64_t,int64_t,int,int) ) + (db_update_i64, void(int,int64_t,int,int) ) + (db_remove_i64, void(int) ) + (db_get_i64, int(int, int, int) ) + (db_next_i64, int(int, int) ) + (db_previous_i64, int(int, int) ) + (db_find_i64, int(int64_t,int64_t,int64_t,int64_t) ) + (db_lowerbound_i64, int(int64_t,int64_t,int64_t,int64_t) ) + (db_upperbound_i64, int(int64_t,int64_t,int64_t,int64_t) ) + (db_end_i64, int(int64_t,int64_t,int64_t) ) DB_SECONDARY_INDEX_METHODS_SIMPLE(idx64) DB_SECONDARY_INDEX_METHODS_SIMPLE(idx128) @@ -1828,8 +1826,8 @@ REGISTER_INTRINSICS(crypto_api, REGISTER_INTRINSICS(permission_api, (check_transaction_authorization, int(int, int, int, int, int, int) ) (check_permission_authorization, int(int64_t, int64_t, int, int, int, int, int64_t) ) - (get_permission_last_used, int64_t(int64_t, int64_t) ) - (get_account_creation_time, int64_t(int64_t) ) + (get_permission_last_used, int64_t(int64_t, int64_t) ) + (get_account_creation_time, int64_t(int64_t) ) ); @@ -1851,15 +1849,15 @@ REGISTER_INTRINSICS(context_free_system_api, REGISTER_INTRINSICS(action_api, (read_action_data, int(int, int) ) (action_data_size, int() ) - (current_receiver, int64_t() ) + (current_receiver, int64_t() ) ); REGISTER_INTRINSICS(authorization_api, - (require_recipient, void(int64_t) ) - (require_authorization, void(int64_t), "require_auth", void(authorization_api::*)(const account_name&) ) - (require_authorization2, void(int64_t, int64_t), "require_auth2", void(authorization_api::*)(const account_name&, const permission_name& permission) ) - (has_authorization, int(int64_t), "has_auth", bool(authorization_api::*)(const account_name&)const ) - (is_account, int(int64_t) ) + (require_recipient, void(int64_t) ) + (require_auth, void(int64_t) ) + (require_auth2, void(int64_t, int64_t) ) + (has_auth, int(int64_t) ) + (is_account, int(int64_t) ) ); REGISTER_INTRINSICS(console_api, @@ -1877,19 +1875,19 @@ REGISTER_INTRINSICS(console_api, ); REGISTER_INTRINSICS(context_free_transaction_api, - (read_transaction, int(int, int) ) - (transaction_size, int() ) - (expiration, int() ) - (tapos_block_prefix, int() ) - (tapos_block_num, int() ) - (get_action, int (int, int, int, int) ) + (read_transaction, int(int, int) ) + (transaction_size, int() ) + (expiration, int() ) + (tapos_block_prefix, int() ) + (tapos_block_num, int() ) + (get_action, int(int, int, int, int) ) ); REGISTER_INTRINSICS(transaction_api, - (send_inline, void(int, int) ) - (send_context_free_inline, void(int, int) ) + (send_inline, void(int, int) ) + (send_context_free_inline, void(int, int) ) (send_deferred, void(int, int64_t, int, int, int32_t) ) - (cancel_deferred, int(int) ) + (cancel_deferred, int(int) ) ); REGISTER_INTRINSICS(context_free_api, diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 81b5c40dad8..6af6dba2e43 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -24,7 +24,6 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - std::cout << "CTX " << &context << "\n"; const auto& res = _instantiated_module->call(&context, "env", "apply", (uint64_t)context.get_receiver(), (uint64_t)context.get_action().account, From 2f3101b652d4c2835bf7e0a14ea2c5ec6515b0ad Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 14 May 2019 23:37:58 +0000 Subject: [PATCH 14/88] update wasm_interface to use uint32_t instead of size_t --- CMakeLists.txt | 1 + .../include/eosio/chain/wasm_interface.hpp | 4 +- .../eosio/chain/wasm_interface_private.hpp | 4 +- .../eosio/chain/webassembly/eos-vm.hpp | 10 +- .../include/eosio/chain/webassembly/wabt.hpp | 12 +- .../include/eosio/chain/webassembly/wavm.hpp | 12 +- libraries/chain/wasm_interface.cpp | 106 +++++++++--------- libraries/chain/webassembly/eos-vm.cpp | 4 +- tests/CMakeLists.txt | 2 - unittests/CMakeLists.txt | 3 +- unittests/api_tests.cpp | 4 +- unittests/producer_schedule_tests.cpp | 1 - 12 files changed, 80 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fb920049c4..8028e200af9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ FIND_PACKAGE(Boost 1.67 REQUIRED COMPONENTS unit_test_framework iostreams) +add_definitions(-DBOOST_COMPUTE_USE_CPP11) add_definitions(-DBOOST_ASIO_DISABLE_STD_EXPERIMENTAL_STRING_VIEW) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 7c2bfa9eb75..ee124f9bca4 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "Runtime/Linker.h" #include "Runtime/Runtime.h" @@ -88,7 +88,7 @@ namespace eosio { namespace chain { static void validate(const controller& control, const bytes& code); //get the wasm_allocator used for the linear memory for wasm - static wasm_backend::wasm_allocator* get_wasm_allocator(); + static vm::wasm_allocator* get_wasm_allocator(); //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 85a3c7f34ae..225e44dd971 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -17,11 +17,11 @@ #include "WAST/WAST.h" #include "IR/Validate.h" -#include +#include using namespace fc; using namespace eosio::chain::webassembly; -using namespace eosio::wasm_backend; +using namespace eosio::vm; using namespace IR; using namespace Runtime; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 539448ad138..f6d9ca51a85 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -7,10 +7,10 @@ #include //eos-vm includes -#include +#include // eosio specific specializations -namespace eosio { namespace wasm_backend { +namespace eosio { namespace vm { template <> struct reduce_type { typedef uint64_t type; @@ -60,12 +60,12 @@ namespace eosio { namespace wasm_backend { return eosio::chain::null_terminated_ptr((char*)(walloc->template get_base_ptr()+val.data.ui)); } -}} // ns eosio::wasm_backend +}} // ns eosio::vm namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { using namespace fc; -using namespace eosio::wasm_backend; +using namespace eosio::vm; using namespace eosio::chain::webassembly::common; class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { @@ -85,4 +85,4 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { #define _EOS_VM_INTRINSIC_NAME(LBL, SUF) __INTRINSIC_NAME(LBL, SUF) #define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ - eosio::wasm_backend::registered_function _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); + eosio::vm::registered_function _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 040505b014b..5dc3c0aeaa5 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -362,9 +362,9 @@ struct intrinsic_invoker_impl> { * @tparam Inputs - the remaining native parameters to transcribe */ template -struct intrinsic_invoker_impl, size_t, Inputs...>> { +struct intrinsic_invoker_impl, uint32_t, Inputs...>> { using next_step = intrinsic_invoker_impl>; - using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, size_t, Inputs..., const TypedValues&, int); + using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, uint32_t, Inputs..., const TypedValues&, int); template static auto translate_one(wabt_apply_instance_vars& vars, Inputs... rest, const TypedValues& args, int offset) -> std::enable_if_t::value, Ret> { @@ -442,9 +442,9 @@ struct intrinsic_invoker_impl> { * @tparam Inputs - the remaining native parameters to transcribe */ template -struct intrinsic_invoker_impl, array_ptr, size_t, Inputs...>> { +struct intrinsic_invoker_impl, array_ptr, uint32_t, Inputs...>> { using next_step = intrinsic_invoker_impl>; - using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, array_ptr, size_t, Inputs..., const TypedValues&, int); + using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, array_ptr, uint32_t, Inputs..., const TypedValues&, int); template static Ret translate_one(wabt_apply_instance_vars& vars, Inputs... rest, const TypedValues& args, int offset) { @@ -468,9 +468,9 @@ struct intrinsic_invoker_impl, array_ptr, size_t * @tparam Inputs - the remaining native parameters to transcribe */ template -struct intrinsic_invoker_impl, int, size_t>> { +struct intrinsic_invoker_impl, int, uint32_t>> { using next_step = intrinsic_invoker_impl>; - using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, int, size_t, const TypedValues&, int); + using then_type = Ret(*)(wabt_apply_instance_vars&, array_ptr, int, uint32_t, const TypedValues&, int); template static Ret translate_one(wabt_apply_instance_vars& vars, const TypedValues& args, int offset) { diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 04c36e8f153..1e91ed228f8 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -377,9 +377,9 @@ struct intrinsic_invoker_impl, std::tuple -struct intrinsic_invoker_impl, size_t, Inputs...>, std::tuple> { +struct intrinsic_invoker_impl, uint32_t, Inputs...>, std::tuple> { using next_step = intrinsic_invoker_impl, std::tuple>; - using then_type = Ret(*)(running_instance_context&, array_ptr, size_t, Inputs..., Translated...); + using then_type = Ret(*)(running_instance_context&, array_ptr, uint32_t, Inputs..., Translated...); template static auto translate_one(running_instance_context& ctx, Inputs... rest, Translated... translated, I32 ptr, I32 size) -> std::enable_if_t::value, Ret> { @@ -456,9 +456,9 @@ struct intrinsic_invoker_impl, s * @tparam Translated - the list of transcribed wasm parameters */ template -struct intrinsic_invoker_impl, array_ptr, size_t, Inputs...>, std::tuple> { +struct intrinsic_invoker_impl, array_ptr, uint32_t, Inputs...>, std::tuple> { using next_step = intrinsic_invoker_impl, std::tuple>; - using then_type = Ret(*)(running_instance_context&, array_ptr, array_ptr, size_t, Inputs..., Translated...); + using then_type = Ret(*)(running_instance_context&, array_ptr, array_ptr, uint32_t, Inputs..., Translated...); template static Ret translate_one(running_instance_context& ctx, Inputs... rest, Translated... translated, I32 ptr_t, I32 ptr_u, I32 size) { @@ -481,9 +481,9 @@ struct intrinsic_invoker_impl, array_ptr, size_t * @tparam Translated - the list of transcribed wasm parameters */ template -struct intrinsic_invoker_impl, int, size_t>, std::tuple<>> { +struct intrinsic_invoker_impl, int, uint32_t>, std::tuple<>> { using next_step = intrinsic_invoker_impl, std::tuple>; - using then_type = Ret(*)(running_instance_context&, array_ptr, int, size_t); + using then_type = Ret(*)(running_instance_context&, array_ptr, int, uint32_t); template static Ret translate_one(running_instance_context& ctx, I32 ptr, I32 value, I32 size) { diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 8bc9c327919..39970c55754 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include namespace eosio { namespace chain { using namespace webassembly; @@ -116,7 +116,7 @@ class context_free_api : public context_aware_api { EOS_ASSERT( context.is_context_free(), unaccessible_api, "this API may only be called from context_free apply" ); } - int get_context_free_data( uint32_t index, array_ptr buffer, size_t buffer_size )const { + int get_context_free_data( uint32_t index, array_ptr buffer, uint32_t buffer_size )const { return context.get_context_free_data( index, buffer, buffer_size ); } }; @@ -181,7 +181,7 @@ class privileged_api : public context_aware_api { context.control.get_resource_limits_manager().get_account_limits( account, ram_bytes, net_weight, cpu_weight); } - int64_t set_proposed_producers( array_ptr packed_producer_schedule, size_t datalen) { + int64_t set_proposed_producers( array_ptr packed_producer_schedule, uint32_t datalen) { datastream ds( packed_producer_schedule, datalen ); vector producers; fc::raw::unpack(ds, producers); @@ -204,7 +204,7 @@ class privileged_api : public context_aware_api { return context.control.set_proposed_producers( std::move(producers) ); } - uint32_t get_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, size_t buffer_size) { + uint32_t get_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, uint32_t buffer_size) { auto& gpo = context.control.get_global_properties(); auto s = fc::raw::pack_size( gpo.configuration ); @@ -218,7 +218,7 @@ class privileged_api : public context_aware_api { return 0; } - void set_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, size_t datalen) { + void set_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, uint32_t datalen) { datastream ds( packed_blockchain_parameters, datalen ); chain::chain_config cfg; fc::raw::unpack(ds, cfg); @@ -707,14 +707,14 @@ class producer_api : public context_aware_api { public: using context_aware_api::context_aware_api; - int get_active_producers(array_ptr producers, size_t buffer_size) { + int get_active_producers(array_ptr producers, uint32_t buffer_size) { auto active_producers = context.get_active_producers(); size_t len = active_producers.size(); auto s = len * sizeof(chain::account_name); if( buffer_size == 0 ) return s; - auto copy_size = std::min( buffer_size, s ); + auto copy_size = std::min( static_cast(buffer_size), s ); memcpy( producers, active_producers.data(), copy_size ); return copy_size; @@ -730,8 +730,8 @@ class crypto_api : public context_aware_api { * no possible side effects other than "passing". */ void assert_recover_key( const fc::sha256& digest, - array_ptr sig, size_t siglen, - array_ptr pub, size_t publen ) { + array_ptr sig, uint32_t siglen, + array_ptr pub, uint32_t publen ) { fc::crypto::signature s; fc::crypto::public_key p; datastream ds( sig, siglen ); @@ -745,8 +745,8 @@ class crypto_api : public context_aware_api { } int recover_key( const fc::sha256& digest, - array_ptr sig, size_t siglen, - array_ptr pub, size_t publen ) { + array_ptr sig, uint32_t siglen, + array_ptr pub, uint32_t publen ) { fc::crypto::signature s; datastream ds( sig, siglen ); datastream pubds( pub, publen ); @@ -756,7 +756,7 @@ class crypto_api : public context_aware_api { return pubds.tellp(); } - template auto encode(char* data, size_t datalen) { + template auto encode(char* data, uint32_t datalen) { Encoder e; const size_t bs = eosio::chain::config::hashing_checktime_block_size; while ( datalen > bs ) { @@ -769,39 +769,39 @@ class crypto_api : public context_aware_api { return e.result(); } - void assert_sha256(array_ptr data, size_t datalen, const fc::sha256& hash_val) { + void assert_sha256(array_ptr data, uint32_t datalen, const fc::sha256& hash_val) { auto result = encode( data, datalen ); EOS_ASSERT( result == hash_val, crypto_api_exception, "hash mismatch" ); } - void assert_sha1(array_ptr data, size_t datalen, const fc::sha1& hash_val) { + void assert_sha1(array_ptr data, uint32_t datalen, const fc::sha1& hash_val) { auto result = encode( data, datalen ); EOS_ASSERT( result == hash_val, crypto_api_exception, "hash mismatch" ); } - void assert_sha512(array_ptr data, size_t datalen, const fc::sha512& hash_val) { + void assert_sha512(array_ptr data, uint32_t datalen, const fc::sha512& hash_val) { auto result = encode( data, datalen ); EOS_ASSERT( result == hash_val, crypto_api_exception, "hash mismatch" ); } - void assert_ripemd160(array_ptr data, size_t datalen, const fc::ripemd160& hash_val) { + void assert_ripemd160(array_ptr data, uint32_t datalen, const fc::ripemd160& hash_val) { auto result = encode( data, datalen ); EOS_ASSERT( result == hash_val, crypto_api_exception, "hash mismatch" ); } - void sha1(array_ptr data, size_t datalen, fc::sha1& hash_val) { + void sha1(array_ptr data, uint32_t datalen, fc::sha1& hash_val) { hash_val = encode( data, datalen ); } - void sha256(array_ptr data, size_t datalen, fc::sha256& hash_val) { + void sha256(array_ptr data, uint32_t datalen, fc::sha256& hash_val) { hash_val = encode( data, datalen ); } - void sha512(array_ptr data, size_t datalen, fc::sha512& hash_val) { + void sha512(array_ptr data, uint32_t datalen, fc::sha512& hash_val) { hash_val = encode( data, datalen ); } - void ripemd160(array_ptr data, size_t datalen, fc::ripemd160& hash_val) { + void ripemd160(array_ptr data, uint32_t datalen, fc::ripemd160& hash_val) { hash_val = encode( data, datalen ); } }; @@ -810,9 +810,9 @@ class permission_api : public context_aware_api { public: using context_aware_api::context_aware_api; - bool check_transaction_authorization( array_ptr trx_data, size_t trx_size, - array_ptr pubkeys_data, size_t pubkeys_size, - array_ptr perms_data, size_t perms_size + bool check_transaction_authorization( array_ptr trx_data, uint32_t trx_size, + array_ptr pubkeys_data, uint32_t pubkeys_size, + array_ptr perms_data, uint32_t perms_size ) { transaction trx = fc::raw::unpack( trx_data, trx_size ); @@ -840,8 +840,8 @@ class permission_api : public context_aware_api { } bool check_permission_authorization( account_name account, permission_name permission, - array_ptr pubkeys_data, size_t pubkeys_size, - array_ptr perms_data, size_t perms_size, + array_ptr pubkeys_data, uint32_t pubkeys_size, + array_ptr perms_data, uint32_t perms_size, uint64_t delay_us ) { @@ -884,14 +884,14 @@ class permission_api : public context_aware_api { } private: - void unpack_provided_keys( flat_set& keys, const char* pubkeys_data, size_t pubkeys_size ) { + void unpack_provided_keys( flat_set& keys, const char* pubkeys_data, uint32_t pubkeys_size ) { keys.clear(); if( pubkeys_size == 0 ) return; keys = fc::raw::unpack>( pubkeys_data, pubkeys_size ); } - void unpack_provided_permissions( flat_set& permissions, const char* perms_data, size_t perms_size ) { + void unpack_provided_permissions( flat_set& permissions, const char* perms_data, uint32_t perms_size ) { permissions.clear(); if( perms_size == 0 ) return; @@ -971,7 +971,7 @@ class context_free_system_api : public context_aware_api { } } - void eosio_assert_message( bool condition, array_ptr msg, size_t msg_len ) { + void eosio_assert_message( bool condition, array_ptr msg, uint32_t msg_len ) { if( BOOST_UNLIKELY( !condition ) ) { const size_t sz = msg_len > max_assert_message ? max_assert_message : msg_len; std::string message( msg, sz ); @@ -1001,15 +1001,13 @@ class action_api : public context_aware_api { action_api( apply_context& ctx ) :context_aware_api(ctx,true){} - int read_action_data(array_ptr memory, size_t buffer_size) __attribute__((target("no-sse"))) { + int read_action_data(array_ptr memory, uint32_t buffer_size) { auto s = context.get_action().data.size(); if( buffer_size == 0 ) return s; - auto copy_size = std::min( buffer_size, s ); + auto copy_size = std::min( static_cast(buffer_size), s ); const char* d = context.get_action().data.data(); - for (int i=0; i < copy_size; i++) - ((char*)memory.value)[i] = (context.get_action().data.data())[i]; - //memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); + memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); return copy_size; } @@ -1036,7 +1034,7 @@ class console_api : public context_aware_api { } } - void prints_l(array_ptr str, size_t str_len ) { + void prints_l(array_ptr str, uint32_t str_len ) { if ( !ignore ) { context.console_append(string(str, str_len)); } @@ -1149,7 +1147,7 @@ class console_api : public context_aware_api { } } - void printhex(array_ptr data, size_t data_len ) { + void printhex(array_ptr data, uint32_t data_len ) { if ( !ignore ) { context.console_append(fc::to_hex(data, data_len)); } @@ -1192,14 +1190,14 @@ class console_api : public context_aware_api { } #define DB_API_METHOD_WRAPPERS_ARRAY_SECONDARY(IDX, ARR_SIZE, ARR_ELEMENT_TYPE)\ - int db_##IDX##_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, array_ptr data, size_t data_len) {\ + int db_##IDX##_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, array_ptr data, uint32_t data_len) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ ("given",data_len)("expected",ARR_SIZE) );\ return context.IDX.store(scope, table, payer, id, data.value);\ }\ - void db_##IDX##_update( int iterator, uint64_t payer, array_ptr data, size_t data_len ) {\ + void db_##IDX##_update( int iterator, uint64_t payer, array_ptr data, uint32_t data_len ) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ @@ -1209,28 +1207,28 @@ class console_api : public context_aware_api { void db_##IDX##_remove( int iterator ) {\ return context.IDX.remove(iterator);\ }\ - int db_##IDX##_find_secondary( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, size_t data_len, uint64_t& primary ) {\ + int db_##IDX##_find_secondary( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, uint32_t data_len, uint64_t& primary ) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ ("given",data_len)("expected",ARR_SIZE) );\ return context.IDX.find_secondary(code, scope, table, data, primary);\ }\ - int db_##IDX##_find_primary( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, size_t data_len, uint64_t primary ) {\ + int db_##IDX##_find_primary( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, uint32_t data_len, uint64_t primary ) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ ("given",data_len)("expected",ARR_SIZE) );\ return context.IDX.find_primary(code, scope, table, data.value, primary);\ }\ - int db_##IDX##_lowerbound( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, size_t data_len, uint64_t& primary ) {\ + int db_##IDX##_lowerbound( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, uint32_t data_len, uint64_t& primary ) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ ("given",data_len)("expected",ARR_SIZE) );\ return context.IDX.lowerbound_secondary(code, scope, table, data.value, primary);\ }\ - int db_##IDX##_upperbound( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, size_t data_len, uint64_t& primary ) {\ + int db_##IDX##_upperbound( uint64_t code, uint64_t scope, uint64_t table, array_ptr data, uint32_t data_len, uint64_t& primary ) {\ EOS_ASSERT( data_len == ARR_SIZE,\ db_api_exception,\ "invalid size of secondary key array for " #IDX ": given ${given} bytes but expected ${expected} bytes",\ @@ -1288,16 +1286,16 @@ class database_api : public context_aware_api { public: using context_aware_api::context_aware_api; - int db_store_i64( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, array_ptr buffer, size_t buffer_size ) { + int db_store_i64( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, array_ptr buffer, uint32_t buffer_size ) { return context.db_store_i64( scope, table, payer, id, buffer, buffer_size ); } - void db_update_i64( int itr, uint64_t payer, array_ptr buffer, size_t buffer_size ) { + void db_update_i64( int itr, uint64_t payer, array_ptr buffer, uint32_t buffer_size ) { context.db_update_i64( itr, payer, buffer, buffer_size ); } void db_remove_i64( int itr ) { context.db_remove_i64( itr ); } - int db_get_i64( int itr, array_ptr buffer, size_t buffer_size ) { + int db_get_i64( int itr, array_ptr buffer, uint32_t buffer_size ) { return context.db_get_i64( itr, buffer, buffer_size ); } int db_next_i64( int itr, uint64_t& primary ) { @@ -1331,17 +1329,17 @@ class memory_api : public context_aware_api { memory_api( apply_context& ctx ) :context_aware_api(ctx,true){} - char* memcpy( array_ptr dest, array_ptr src, size_t length) { + char* memcpy( array_ptr dest, array_ptr src, uint32_t length) { EOS_ASSERT((size_t)(std::abs((ptrdiff_t)dest.value - (ptrdiff_t)src.value)) >= length, overlapping_memory_error, "memcpy can only accept non-aliasing pointers"); return (char *)::memcpy(dest, src, length); } - char* memmove( array_ptr dest, array_ptr src, size_t length) { + char* memmove( array_ptr dest, array_ptr src, uint32_t length) { return (char *)::memmove(dest, src, length); } - int memcmp( array_ptr dest, array_ptr src, size_t length) { + int memcmp( array_ptr dest, array_ptr src, uint32_t length) { int ret = ::memcmp(dest, src, length); if(ret < 0) return -1; @@ -1350,7 +1348,7 @@ class memory_api : public context_aware_api { return 0; } - char* memset( array_ptr dest, int value, size_t length ) { + char* memset( array_ptr dest, int value, uint32_t length ) { return (char *)::memset( dest, value, length ); } }; @@ -1359,7 +1357,7 @@ class transaction_api : public context_aware_api { public: using context_aware_api::context_aware_api; - void send_inline( array_ptr data, size_t data_len ) { + void send_inline( array_ptr data, uint32_t data_len ) { //TODO: Why is this limit even needed? And why is it not consistently checked on actions in input or deferred transactions EOS_ASSERT( data_len < context.control.get_global_properties().configuration.max_inline_action_size, inline_action_too_big, "inline action too big" ); @@ -1369,7 +1367,7 @@ class transaction_api : public context_aware_api { context.execute_inline(std::move(act)); } - void send_context_free_inline( array_ptr data, size_t data_len ) { + void send_context_free_inline( array_ptr data, uint32_t data_len ) { //TODO: Why is this limit even needed? And why is it not consistently checked on actions in input or deferred transactions EOS_ASSERT( data_len < context.control.get_global_properties().configuration.max_inline_action_size, inline_action_too_big, "inline action too big" ); @@ -1379,7 +1377,7 @@ class transaction_api : public context_aware_api { context.execute_context_free_inline(std::move(act)); } - void send_deferred( const uint128_t& sender_id, account_name payer, array_ptr data, size_t data_len, uint32_t replace_existing) { + void send_deferred( const uint128_t& sender_id, account_name payer, array_ptr data, uint32_t data_len, uint32_t replace_existing) { transaction trx; fc::raw::unpack(data, data_len, trx); context.schedule_deferred_transaction(sender_id, payer, std::move(trx), replace_existing); @@ -1397,13 +1395,13 @@ class context_free_transaction_api : public context_aware_api { context_free_transaction_api( apply_context& ctx ) :context_aware_api(ctx,true){} - int read_transaction( array_ptr data, size_t buffer_size ) { + int read_transaction( array_ptr data, uint32_t buffer_size ) { bytes trx = context.get_packed_transaction(); auto s = trx.size(); if( buffer_size == 0) return s; - auto copy_size = std::min( buffer_size, s ); + auto copy_size = std::min( static_cast(buffer_size), s ); memcpy( data, trx.data(), copy_size ); return copy_size; @@ -1424,7 +1422,7 @@ class context_free_transaction_api : public context_aware_api { return context.trx_context.trx.ref_block_prefix; } - int get_action( uint32_t type, uint32_t index, array_ptr buffer, size_t buffer_size )const { + int get_action( uint32_t type, uint32_t index, array_ptr buffer, uint32_t buffer_size )const { return context.get_action( type, index, buffer, buffer_size ); } }; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 6af6dba2e43..c47c10b3c81 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -3,11 +3,11 @@ #include #include //eos-vm includes -#include +#include namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { -using namespace eosio::wasm_backend; +using namespace eosio::vm; namespace wasm_constraints = eosio::chain::wasm_constraints; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba87969bb80..7e770d7c24d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,8 +9,6 @@ find_package(LLVM 4.0 REQUIRED CONFIG) link_directories(${LLVM_LIBRARY_DIR}) -set( CMAKE_CXX_STANDARD 14 ) - include_directories("${CMAKE_SOURCE_DIR}/plugins/wallet_plugin/include") file(GLOB UNIT_TESTS "*.cpp") diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index b0a08280336..3d4aacd5cdf 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -33,8 +33,7 @@ find_package(LLVM 4.0 REQUIRED CONFIG) link_directories(${LLVM_LIBRARY_DIR}) -set( CMAKE_CXX_STANDARD 14 ) - +add_definitions(-DNON_VALIDATING_TEST) add_subdirectory(contracts) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/contracts.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/include/contracts.hpp ESCAPE_QUOTES) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 206d3878ff2..13d3ca28528 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -115,6 +115,7 @@ FC_REFLECT( u128_action, (values) ) FC_REFLECT( cf_action, (payload)(cfd_idx) ) FC_REFLECT( dtt_action, (payer)(deferred_account)(deferred_action)(permission_name)(delay_sec) ) FC_REFLECT( invalid_access_action, (code)(val)(index)(store) ) +#define NON_VALIDATING_TEST 1 #ifdef NON_VALIDATING_TEST #define TESTER tester @@ -589,6 +590,7 @@ BOOST_FIXTURE_TEST_CASE(require_notice_tests, TESTER) { try { } FC_LOG_AND_RETHROW() } +/* BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try { validating_tester chain( validating_tester::default_config() ); chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios ); @@ -615,7 +617,7 @@ BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try { BOOST_REQUIRE_EQUAL( chain.validate(), true ); } FC_LOG_AND_RETHROW() } - +*/ /************************************************************************************* * context free action tests *************************************************************************************/ diff --git a/unittests/producer_schedule_tests.cpp b/unittests/producer_schedule_tests.cpp index 2a91f06a7c1..bec63376ff5 100644 --- a/unittests/producer_schedule_tests.cpp +++ b/unittests/producer_schedule_tests.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include "fork_test_utilities.hpp" From 8e28a4502fa5df436f5d7935ba6d371012187dc4 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 17 May 2019 19:02:33 -0400 Subject: [PATCH 15/88] create eosio::chain::intrinsic_debug_log Add new suite of unit tests (intrinsic_debug_log_tests) to test the intrinsic_debug_log. For now there is only one basic test. --- libraries/chain/CMakeLists.txt | 1 + .../eosio/chain/intrinsic_debug_log.hpp | 46 ++++ libraries/chain/intrinsic_debug_log.cpp | 232 ++++++++++++++++++ unittests/intrinsic_debug_log_tests.cpp | 58 +++++ 4 files changed, 337 insertions(+) create mode 100644 libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp create mode 100644 libraries/chain/intrinsic_debug_log.cpp create mode 100644 unittests/intrinsic_debug_log_tests.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 254d462c5ed..75b5cb18dea 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -52,6 +52,7 @@ add_library( eosio_chain genesis_intrinsics.cpp whitelisted_intrinsics.cpp thread_utils.cpp + intrinsic_debug_log.cpp ${HEADERS} ) diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp new file mode 100644 index 00000000000..be49c063761 --- /dev/null +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -0,0 +1,46 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#pragma once + +#include + +namespace boost { + namespace filesystem { + class path; + } +} + +namespace eosio { namespace chain { + + namespace detail { class intrinsic_debug_log_impl; } + + class intrinsic_debug_log { + public: + intrinsic_debug_log( const boost::filesystem::path& log_path ); + intrinsic_debug_log( intrinsic_debug_log&& other ); + ~intrinsic_debug_log(); + + enum class open_mode { + read_only, + create_new, + continue_existing + }; + + void open( open_mode mode = open_mode::continue_existing ); + void close(); + void flush(); + + void start_block( uint32_t block_num ); + void start_transaction( const transaction_id_type& trx_id ); + void start_action( uint64_t global_sequence_num ); + void acknowledge_intrinsic_without_recording(); + void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); + void finish_block(); + + private: + std::unique_ptr my; + }; + +} } diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp new file mode 100644 index 00000000000..2a08b92f83b --- /dev/null +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -0,0 +1,232 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#include +#include +#include +#include +#include + +namespace bfs = boost::filesystem; + +namespace eosio { namespace chain { + + namespace detail { + class intrinsic_debug_log_impl { + public: + enum class state_t { + closed, + read_only, + waiting_to_start_block, + in_block, + in_transaction, + in_action + }; + + static const uint8_t block_tag = 0; + static const uint8_t transaction_tag = 1; + static const uint8_t action_tag = 2; + static const uint8_t intrinsic_tag = 3; + + std::fstream log; + bfs::path log_path; + state_t state = state_t::closed; + uint64_t last_commited_block_pos = 0; + uint64_t most_recent_block_pos = 0; + uint32_t most_recent_block_num = 0; + uint32_t intrinsic_counter = 0; + bool written_start_block = false; + + void open( bool read_only, bool start_fresh ); + void close(); + + void start_block( uint32_t block_num ); + void ensure_block_start_written(); + void finish_block(); + }; + + void intrinsic_debug_log_impl::open( bool read_only, bool start_fresh ) { + FC_ASSERT( !log.is_open(), "cannot open when the log is already open" ); + FC_ASSERT( state == state_t::closed, "state out of sync" ); + + std::ios_base::openmode open_mode = std::ios::in | std::ios::binary; + + if( !read_only ) { + open_mode |= (std::ios::out | std::ios::app); + } else { + FC_ASSERT( !start_fresh, "start_fresh cannot be true in read-only mode" ); + } + + if( start_fresh ) { + open_mode |= std::ios::trunc; + } + + log.open( log_path.generic_string().c_str(), open_mode ); + state = state_t::waiting_to_start_block; + + log.seekg( 0, std::ios::end ); + auto file_size = log.tellg(); + if( file_size > 0 ) { + log.seekg( -sizeof(last_commited_block_pos), std::ios::end ); + log.read( (char*)&last_commited_block_pos, sizeof(last_commited_block_pos) ); + FC_ASSERT( last_commited_block_pos < file_size, "corrupted log: invalid block pos" ); + most_recent_block_pos = last_commited_block_pos; + + log.seekg( last_commited_block_pos, std::ios::beg ); + uint8_t tag = 0; + log.read( (char*)&tag, sizeof(tag) ); + FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); + + log.read( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); + } + log.seekg( 0, std::ios::beg ); + } + + void intrinsic_debug_log_impl::close() { + if( !log.is_open() ) return; + FC_ASSERT( state != state_t::closed, "state out of sync" ); + + log.flush(); + log.close(); + if( state != state_t::waiting_to_start_block ) { + bfs::resize_file( log_path, last_commited_block_pos ); + } + state = state_t::closed; + intrinsic_counter = 0; + written_start_block = false; + } + + void intrinsic_debug_log_impl::start_block( uint32_t block_num ) { + FC_ASSERT( state == state_t::waiting_to_start_block, "cannot start block while still processing a block" ); + FC_ASSERT( most_recent_block_num < block_num, + "must start a block with greater height than previously started blocks" ); + + state = state_t::in_block; + most_recent_block_pos = log.tellg(); + most_recent_block_num = block_num; + } + + void intrinsic_debug_log_impl::ensure_block_start_written() { + if( written_start_block ) return; + FC_ASSERT( state == state_t::in_block, "called while in invalid state" ); + log.put( block_tag ); + log.write( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); + written_start_block = true; + } + + void intrinsic_debug_log_impl::finish_block() { + if( written_start_block ) { + log.put( block_tag ); + log.write( (char*)&most_recent_block_pos, sizeof(most_recent_block_pos) ); + } + state = state_t::waiting_to_start_block; + intrinsic_counter = 0; + written_start_block = false; + } + } + + intrinsic_debug_log::intrinsic_debug_log( const bfs::path& log_path ) + :my( new detail::intrinsic_debug_log_impl() ) { + my->log_path = log_path; + my->log.exceptions( std::fstream::failbit | std::fstream::badbit ); + } + + intrinsic_debug_log::intrinsic_debug_log( intrinsic_debug_log&& other) { + my = std::move(other.my); + } + + intrinsic_debug_log::~intrinsic_debug_log() { + if( my ) { + my->close(); + my.reset(); + } + } + + void intrinsic_debug_log::open( open_mode mode ) { + bool read_only = (mode == open_mode::read_only); + my->open( read_only, ( read_only ? false : (mode == open_mode::create_new) ) ); + } + + void intrinsic_debug_log::close() { + my->close(); + } + + void intrinsic_debug_log::flush() { + my->log.flush(); + } + + void intrinsic_debug_log::start_block( uint32_t block_num ) { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + my->start_block( block_num ); + } + + void intrinsic_debug_log::finish_block() { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + my->finish_block(); + } + + void intrinsic_debug_log::start_transaction( const transaction_id_type& trx_id ) { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::closed + && my->state != detail::intrinsic_debug_log_impl::state_t::waiting_to_start_block, + "cannot start transaction in the current state" ); + + char buffer[32]; + fc::datastream ds( buffer, sizeof(buffer) ); + fc::raw::pack(ds, trx_id); + FC_ASSERT( !ds.remaining(), "unexpected size for transaction id" ); + + my->ensure_block_start_written(); + my->log.put( detail::intrinsic_debug_log_impl::transaction_tag ); + my->log.write( buffer, sizeof(buffer) ); + + my->state = detail::intrinsic_debug_log_impl::state_t::in_transaction; + my->intrinsic_counter = 0; + } + + void intrinsic_debug_log::start_action( uint64_t global_sequence_num ) { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_transaction + || my->state == detail::intrinsic_debug_log_impl::state_t::in_action, + "cannot start action in the current state" ); + + my->log.put( detail::intrinsic_debug_log_impl::action_tag ); + my->log.write( (char*)&global_sequence_num, sizeof(global_sequence_num) ); + + my->state = detail::intrinsic_debug_log_impl::state_t::in_action; + my->intrinsic_counter = 0; + } + + void intrinsic_debug_log::acknowledge_intrinsic_without_recording() { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_action, + "can only acknowledge intrinsic within action state" ); + ++(my->intrinsic_counter); + } + + void intrinsic_debug_log::record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ) { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, + "invalid operation in read-only mode" ); + FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_action, + "can only record intrinsic within action state" ); + + char buffer[64]; + fc::datastream ds( buffer, sizeof(buffer) ); + fc::raw::pack(ds, arguments_hash); + fc::raw::pack(ds, memory_hash); + FC_ASSERT( !ds.remaining(), "unexpected size for digest" ); + + my->log.put( detail::intrinsic_debug_log_impl::intrinsic_tag ); + my->log.write( (char*)&my->intrinsic_counter, sizeof(my->intrinsic_counter) ); + my->log.write( buffer, sizeof(buffer) ); + + ++(my->intrinsic_counter); + } + +} } /// eosio::chain diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp new file mode 100644 index 00000000000..598bc1e0ca4 --- /dev/null +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -0,0 +1,58 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#include + +#include +#include +#include + +using namespace eosio; +using namespace chain; + +BOOST_AUTO_TEST_SUITE(intrinsic_debug_log_tests) + +BOOST_AUTO_TEST_CASE(basic_test) { + fc::temp_directory tempdir; + auto log_path = tempdir.path() / "intrinsic.log"; + { + intrinsic_debug_log log( log_path ); + log.open(); + + log.start_block( 2u ); + log.start_transaction( transaction_id_type() ); + log.start_action( 1ull ); + log.acknowledge_intrinsic_without_recording(); + + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + + log.record_intrinsic( digest, digest ); + log.finish_block(); + log.close(); + } + + std::fstream log_stream( log_path.generic_string().c_str(), std::ios::in | std::ios::binary ); + log_stream.seekg( 0, std::ios::end ); + std::size_t file_size = log_stream.tellg(); + log_stream.seekg( 0, std::ios::beg ); + + vector buffer( file_size ); + log_stream.read( buffer.data(), file_size ); + log_stream.close(); + + const char* expected = + "00" "02000000" // start of block 2 + "01" "0000000000000000000000000000000000000000000000000000000000000000" // start of transaction + "02" "0100000000000000" // start of action with global sequence number of 1 + "03" // recording an intrinsic with: + "01000000" // an ordinal of 1, + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" // this arguments hash, + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" // and this WASM lineary memory hash. + "00" "0000000000000000" // end of the block with pointer to position of the start of the block + ; + + BOOST_CHECK_EQUAL( fc::to_hex( buffer ), expected ); +} + +BOOST_AUTO_TEST_SUITE_END() From bc1fe81b94465d496d7e17a311810c0c0a1b9e74 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 17 May 2019 22:42:10 -0400 Subject: [PATCH 16/88] add support for iterating through the blocks tracked by intrinsic_debug_log --- .../eosio/chain/intrinsic_debug_log.hpp | 96 ++++++++- libraries/chain/intrinsic_debug_log.cpp | 199 +++++++++++++++++- unittests/intrinsic_debug_log_tests.cpp | 73 ++++++- 3 files changed, 355 insertions(+), 13 deletions(-) diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index be49c063761..76c0f1ebf9c 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include namespace boost { namespace filesystem { @@ -14,7 +15,10 @@ namespace boost { namespace eosio { namespace chain { - namespace detail { class intrinsic_debug_log_impl; } + namespace detail { + struct extended_block_data; + class intrinsic_debug_log_impl; + } class intrinsic_debug_log { public: @@ -29,9 +33,11 @@ namespace eosio { namespace chain { }; void open( open_mode mode = open_mode::continue_existing ); - void close(); void flush(); + /** invalidates all block_iterators */ + void close(); + void start_block( uint32_t block_num ); void start_transaction( const transaction_id_type& trx_id ); void start_action( uint64_t global_sequence_num ); @@ -39,8 +45,94 @@ namespace eosio { namespace chain { void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); void finish_block(); + struct intrinsic_record { + uint32_t intrinsic_ordinal = 0; + digest_type arguments_hash; + digest_type memory_hash; + }; + + struct action_data { + uint64_t global_sequence_num = 0; + std::vector< intrinsic_record > recorded_intrinsics; + }; + + struct transaction_data { + transaction_id_type trx_id; + std::vector< action_data > actions; + }; + + struct block_data { + uint32_t block_num = 0; + std::vector< transaction_data > transactions; + }; + + class block_iterator : public std::iterator { + protected: + detail::intrinsic_debug_log_impl* _log = nullptr; + int64_t _pos = -1; + mutable const detail::extended_block_data* _cached_block_data = nullptr; + + protected: + explicit block_iterator( detail::intrinsic_debug_log_impl* log, int64_t pos = -1 ) + :_log(log) + ,_pos(pos) + {} + + const block_data* get_pointer()const; + + friend class intrinsic_debug_log; + + public: + block_iterator() = default; + + friend bool operator == ( const block_iterator& lhs, const block_iterator& rhs ) { + return std::tie( lhs._log, lhs._pos ) == std::tie( rhs._log, rhs._pos ); + } + + friend bool operator != ( const block_iterator& lhs, const block_iterator& rhs ) { + return !(lhs == rhs); + } + + const block_data& operator*()const { + return *get_pointer(); + } + + const block_data* operator->()const { + return get_pointer(); + } + + block_iterator& operator++(); + + block_iterator& operator--(); + + block_iterator operator++(int) { + block_iterator result(*this); + ++(*this); + return result; + } + + block_iterator operator--(int) { + block_iterator result(*this); + --(*this); + return result; + } + }; + + using block_reverse_iterator = std::reverse_iterator; + + // the four begin/end functions below can only be called in read-only mode + block_iterator begin_block()const; + block_iterator end_block()const; + block_reverse_iterator rbegin_block()const { return std::make_reverse_iterator( begin_block() ); } + block_reverse_iterator rend_blocks()const { return std::make_reverse_iterator( end_block() ); } + private: std::unique_ptr my; }; } } + +FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_record, (intrinsic_ordinal)(arguments_hash)(memory_hash) ) +FC_REFLECT( eosio::chain::intrinsic_debug_log::action_data, (global_sequence_num)(recorded_intrinsics) ) +FC_REFLECT( eosio::chain::intrinsic_debug_log::transaction_data, (trx_id)(actions) ) +FC_REFLECT( eosio::chain::intrinsic_debug_log::block_data, (block_num)(transactions) ) diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 2a08b92f83b..158e6c51649 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace bfs = boost::filesystem; @@ -13,6 +14,21 @@ namespace bfs = boost::filesystem; namespace eosio { namespace chain { namespace detail { + struct extended_block_data { + extended_block_data( intrinsic_debug_log::block_data&& block_data, + int64_t pos, int64_t previous_pos, int64_t next_pos ) + :block_data( std::move(block_data) ) + ,pos( pos ) + ,previous_pos( previous_pos ) + ,next_pos( next_pos ) + {} + + intrinsic_debug_log::block_data block_data; + int64_t pos; //< pos should never be negative + int64_t previous_pos; //< if previous_pos is -1, then this is first block in the log + int64_t next_pos; //< if next_pos is -1, then this is the last block in the log + }; + class intrinsic_debug_log_impl { public: enum class state_t { @@ -31,7 +47,9 @@ namespace eosio { namespace chain { std::fstream log; bfs::path log_path; + std::map block_data_cache; state_t state = state_t::closed; + int64_t file_size = -1; uint64_t last_commited_block_pos = 0; uint64_t most_recent_block_pos = 0; uint32_t most_recent_block_num = 0; @@ -44,6 +62,9 @@ namespace eosio { namespace chain { void start_block( uint32_t block_num ); void ensure_block_start_written(); void finish_block(); + + int64_t get_position_of_previous_block( int64_t pos ); + const extended_block_data& get_block_at_position( int64_t pos ); }; void intrinsic_debug_log_impl::open( bool read_only, bool start_fresh ) { @@ -63,14 +84,14 @@ namespace eosio { namespace chain { } log.open( log_path.generic_string().c_str(), open_mode ); - state = state_t::waiting_to_start_block; + state = (read_only ? state_t::read_only : state_t::waiting_to_start_block); log.seekg( 0, std::ios::end ); - auto file_size = log.tellg(); + file_size = log.tellg(); if( file_size > 0 ) { log.seekg( -sizeof(last_commited_block_pos), std::ios::end ); log.read( (char*)&last_commited_block_pos, sizeof(last_commited_block_pos) ); - FC_ASSERT( last_commited_block_pos < file_size, "corrupted log: invalid block pos" ); + FC_ASSERT( last_commited_block_pos < file_size, "corrupted log: invalid block position" ); most_recent_block_pos = last_commited_block_pos; log.seekg( last_commited_block_pos, std::ios::beg ); @@ -89,12 +110,15 @@ namespace eosio { namespace chain { log.flush(); log.close(); - if( state != state_t::waiting_to_start_block ) { + if( state != state_t::read_only && state != state_t::waiting_to_start_block ) { bfs::resize_file( log_path, last_commited_block_pos ); } state = state_t::closed; intrinsic_counter = 0; written_start_block = false; + file_size = -1; + + block_data_cache.clear(); } void intrinsic_debug_log_impl::start_block( uint32_t block_num ) { @@ -124,6 +148,106 @@ namespace eosio { namespace chain { intrinsic_counter = 0; written_start_block = false; } + + int64_t intrinsic_debug_log_impl::get_position_of_previous_block( int64_t pos ) { + int64_t previous_pos = -1; + if( pos > sizeof(last_commited_block_pos) ) { + decltype(last_commited_block_pos) read_pos{}; + log.seekg( pos - sizeof(read_pos), std::ios::beg ); + log.read( (char*)&read_pos, sizeof(read_pos) ); + FC_ASSERT( read_pos < log.tellg(), "corrupted log: invalid block position" ); + previous_pos = read_pos; + } + return previous_pos; + } + + const extended_block_data& intrinsic_debug_log_impl::get_block_at_position( int64_t pos ) { + FC_ASSERT( state == state_t::read_only, "only allowed in read-only mode" ); + FC_ASSERT( 0 <= pos && pos < file_size, "position is out of bounds" ); + + auto itr = block_data_cache.find( static_cast(pos) ); + if( itr != block_data_cache.end() ) return itr->second; + + int64_t previous_pos = get_position_of_previous_block( pos ); + log.seekg( pos, std::ios::beg ); + + intrinsic_debug_log::block_data block_data; + + // Read all data for the current block. + { + uint8_t tag = 0; + log.read( (char*)&tag, sizeof(tag) ); + FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); + log.read( (char*)&block_data.block_num, sizeof(block_data.block_num) ); + + bool in_transaction = false; + bool in_action = false; + uint64_t minimum_accepted_global_sequence_num = 0; + + for(;;) { + log.read( (char*)&tag, sizeof(tag) ); + if( tag == block_tag ) { + decltype(last_commited_block_pos) read_pos{}; + log.read( (char*)&read_pos, sizeof(read_pos) ); + FC_ASSERT( read_pos == pos, "corrupted log: block position did not match" ); + break; + } else if( tag == transaction_tag ) { + char buffer[32]; + log.read( buffer, sizeof(buffer) ); + fc::datastream ds( buffer, sizeof(buffer) ); + block_data.transactions.emplace_back(); + fc::raw::unpack( ds, block_data.transactions.back().trx_id ); + in_transaction = true; + in_action = false; + } else if( tag == action_tag ) { + FC_ASSERT( in_transaction, "corrupted log: no start of transaction before encountering action tag"); + uint64_t global_sequence_num = 0; + log.read( (char*)&global_sequence_num, sizeof(global_sequence_num) ); + FC_ASSERT( global_sequence_num >= minimum_accepted_global_sequence_num, + "corrupted log: global sequence numbers of actions within a block are not monotonically increasing" + ); + block_data.transactions.back().actions.emplace_back(); + auto& act = block_data.transactions.back().actions.back(); + act.global_sequence_num = global_sequence_num; + in_action = true; + minimum_accepted_global_sequence_num = global_sequence_num + 1; + } else if( tag == intrinsic_tag ) { + FC_ASSERT( in_action, "corrupted log: no start of action before encountering intrinsic tag"); + auto& intrinsics = block_data.transactions.back().actions.back().recorded_intrinsics; + uint32_t minimum_accepted_ordinal = 0; + if( intrinsics.size() > 0 ) { + minimum_accepted_ordinal = intrinsics.back().intrinsic_ordinal + 1; + } + uint32_t ordinal = 0; + log.read( (char*)&ordinal, sizeof(ordinal) ); + FC_ASSERT( ordinal >= minimum_accepted_ordinal, + "corrupted log: intrinsic ordinals within an action are not monotonically increasing" ); + char buffer[64]; + log.read( buffer, sizeof(buffer) ); + fc::datastream ds( buffer, sizeof(buffer) ); + intrinsics.emplace_back(); + auto& intrinsic = intrinsics.back(); + intrinsic.intrinsic_ordinal = ordinal; + fc::raw::unpack( ds, intrinsic.arguments_hash ); + fc::raw::unpack( ds, intrinsic.memory_hash ); + } else { + FC_ASSERT( false, "corrupted log: unrecognized tag" ); + } + } + + FC_ASSERT( block_data.transactions.size() > 0, "corrupted log: empty block included" ); + } + + int64_t next_pos = -1; + if( log.tellg() < file_size ) next_pos = log.tellg(); + + auto res = block_data_cache.emplace( + std::piecewise_construct, + std::forward_as_tuple( static_cast(pos) ), + std::forward_as_tuple( std::move(block_data), pos, previous_pos, next_pos ) + ); + return res.first->second; + } } intrinsic_debug_log::intrinsic_debug_log( const bfs::path& log_path ) @@ -177,7 +301,7 @@ namespace eosio { namespace chain { char buffer[32]; fc::datastream ds( buffer, sizeof(buffer) ); - fc::raw::pack(ds, trx_id); + fc::raw::pack( ds, trx_id ); FC_ASSERT( !ds.remaining(), "unexpected size for transaction id" ); my->ensure_block_start_written(); @@ -218,8 +342,8 @@ namespace eosio { namespace chain { char buffer[64]; fc::datastream ds( buffer, sizeof(buffer) ); - fc::raw::pack(ds, arguments_hash); - fc::raw::pack(ds, memory_hash); + fc::raw::pack( ds, arguments_hash ); + fc::raw::pack( ds, memory_hash ); FC_ASSERT( !ds.remaining(), "unexpected size for digest" ); my->log.put( detail::intrinsic_debug_log_impl::intrinsic_tag ); @@ -229,4 +353,65 @@ namespace eosio { namespace chain { ++(my->intrinsic_counter); } + intrinsic_debug_log::block_iterator intrinsic_debug_log::begin_block()const { + FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, + "block_begin is only allowed in read-only mode" ); + return block_iterator( my.get(), (my->last_commited_block_pos == 0ull ? -1ll : 0ll) ); + } + + intrinsic_debug_log::block_iterator intrinsic_debug_log::end_block()const { + FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, + "block_end is only allowed in read-only mode" ); + return block_iterator( my.get() ); + } + + const intrinsic_debug_log::block_data* intrinsic_debug_log::block_iterator::get_pointer()const { + FC_ASSERT( _log, "cannot dereference default constructed block iterator" ); + FC_ASSERT( _pos >= 0, "cannot dereference an end block iterator" ); + + if( !_cached_block_data ) { + _cached_block_data = &_log->get_block_at_position( _pos ); + } + + return &(_cached_block_data->block_data); + + } + + intrinsic_debug_log::block_iterator& intrinsic_debug_log::block_iterator::operator++() { + FC_ASSERT( _log, "cannot increment default constructed block iterator" ); + FC_ASSERT( _pos >= 0, "cannot increment end block iterator" ); + + if( !_cached_block_data ) { + _cached_block_data = &_log->get_block_at_position( _pos ); + } + + _pos = _cached_block_data->next_pos; + _cached_block_data = nullptr; + + return *this; + } + + intrinsic_debug_log::block_iterator& intrinsic_debug_log::block_iterator::operator--() { + FC_ASSERT( _log, "cannot decrement default constructed block iterator" ); + + if( _pos < 0 ) { + FC_ASSERT( !_cached_block_data, "invariant failure" ); + FC_ASSERT( _log->file_size > 0, "cannot decrement end block iterator when the log contains no blocks" ); + _pos = static_cast( _log->last_commited_block_pos ); + return *this; + } + + int64_t prev_pos = -1; + if( _cached_block_data ) { + prev_pos = _cached_block_data->previous_pos; + } else { + prev_pos = _log->get_position_of_previous_block( _pos ); + } + + FC_ASSERT( prev_pos >= 0, "cannot decrement begin block iterator" ); + _pos = prev_pos; + _cached_block_data = nullptr; + return *this; + } + } } /// eosio::chain diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index 598bc1e0ca4..fb935ae7455 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -17,16 +18,13 @@ BOOST_AUTO_TEST_CASE(basic_test) { fc::temp_directory tempdir; auto log_path = tempdir.path() / "intrinsic.log"; { + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); intrinsic_debug_log log( log_path ); log.open(); - log.start_block( 2u ); log.start_transaction( transaction_id_type() ); log.start_action( 1ull ); log.acknowledge_intrinsic_without_recording(); - - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - log.record_intrinsic( digest, digest ); log.finish_block(); log.close(); @@ -55,4 +53,71 @@ BOOST_AUTO_TEST_CASE(basic_test) { BOOST_CHECK_EQUAL( fc::to_hex( buffer ), expected ); } +BOOST_AUTO_TEST_CASE(iterate_test) { + fc::temp_directory tempdir; + auto log_path = tempdir.path() / "intrinsic.log"; + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + + intrinsic_debug_log log( log_path ); + log.open(); + log.start_block( 2u ); + log.start_transaction( transaction_id_type() ); + log.start_action( 1ull ); + log.acknowledge_intrinsic_without_recording(); + log.record_intrinsic( digest, digest ); + log.finish_block(); + log.start_block( 4u ); + log.start_transaction( transaction_id_type() ); + log.start_transaction( transaction_id_type() ); + log.start_action( 2ull ); + log.record_intrinsic( digest, digest ); + log.record_intrinsic( digest, digest ); + log.start_action( 3ull ); + log.finish_block(); + log.close(); + + log.open( intrinsic_debug_log::open_mode::read_only ); + std::vector< std::pair< uint32_t, uint32_t > > expected_blocks = { + {2u, 1u}, + {4u, 2u} + }; + + { + auto block_itr = log.begin_block(); + const auto block_end_itr = log.end_block(); + for( auto itr = expected_blocks.begin(); block_itr != block_end_itr; ++block_itr, ++itr ) { + BOOST_REQUIRE( itr != expected_blocks.end() ); + BOOST_CHECK_EQUAL( block_itr->block_num, itr->first ); + BOOST_CHECK_EQUAL( block_itr->transactions.size(), itr->second ); + wdump( (fc::json::to_pretty_string(*block_itr)) ); + } + + for( auto itr = expected_blocks.rbegin(); itr != expected_blocks.rend(); ++itr ) { + --block_itr; + BOOST_CHECK_EQUAL( block_itr->block_num, itr->first ); + BOOST_CHECK_EQUAL( block_itr->transactions.size(), itr->second ); + } + log.close(); // invalidates block_itr; + } + + log.open( intrinsic_debug_log::open_mode::read_only ); + { + auto block_itr = log.end_block(); + --block_itr; + auto itr = expected_blocks.rbegin(); + for( ; itr != expected_blocks.rend() && itr->first > 3u; ++itr ) { + --block_itr; + // Avoid dereferencing block_itr to not read block and add to cache. + } + + BOOST_REQUIRE( itr != expected_blocks.rend() ); + BOOST_REQUIRE_EQUAL( itr->first, 2u ); // ensure we are at the expected block + + // block_itr should also be point to a valid block (the same one with a block height of 2). + // However, it hasn't been cached yet since we have not yet dereferenced it. + + BOOST_REQUIRE_EQUAL( block_itr->block_num, 2u ); + } +} + BOOST_AUTO_TEST_SUITE_END() From dbabba6061bc5cce59962e85d1e489c03e83aeb8 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 17 May 2019 22:59:02 -0400 Subject: [PATCH 17/88] add receiver, first_receiver, and action_name to logged action_data --- .../eosio/chain/intrinsic_debug_log.hpp | 9 +++++++-- libraries/chain/intrinsic_debug_log.cpp | 12 +++++++++-- unittests/intrinsic_debug_log_tests.cpp | 20 ++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index 76c0f1ebf9c..0d30c0b1f80 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -40,7 +40,8 @@ namespace eosio { namespace chain { void start_block( uint32_t block_num ); void start_transaction( const transaction_id_type& trx_id ); - void start_action( uint64_t global_sequence_num ); + void start_action( uint64_t global_sequence_num, + name receiver, name first_receiver, name action_name ); void acknowledge_intrinsic_without_recording(); void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); void finish_block(); @@ -53,6 +54,9 @@ namespace eosio { namespace chain { struct action_data { uint64_t global_sequence_num = 0; + name receiver{}; + name first_receiver{}; + name action_name{}; std::vector< intrinsic_record > recorded_intrinsics; }; @@ -133,6 +137,7 @@ namespace eosio { namespace chain { } } FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_record, (intrinsic_ordinal)(arguments_hash)(memory_hash) ) -FC_REFLECT( eosio::chain::intrinsic_debug_log::action_data, (global_sequence_num)(recorded_intrinsics) ) +FC_REFLECT( eosio::chain::intrinsic_debug_log::action_data, + (global_sequence_num)(receiver)(first_receiver)(action_name)(recorded_intrinsics) ) FC_REFLECT( eosio::chain::intrinsic_debug_log::transaction_data, (trx_id)(actions) ) FC_REFLECT( eosio::chain::intrinsic_debug_log::block_data, (block_num)(transactions) ) diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 158e6c51649..56a427e600d 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -209,6 +209,9 @@ namespace eosio { namespace chain { block_data.transactions.back().actions.emplace_back(); auto& act = block_data.transactions.back().actions.back(); act.global_sequence_num = global_sequence_num; + log.read( (char*)&act.receiver.value, sizeof(act.receiver.value) ); + log.read( (char*)&act.first_receiver.value, sizeof(act.first_receiver.value) ); + log.read( (char*)&act.action_name.value, sizeof(act.action_name.value) ); in_action = true; minimum_accepted_global_sequence_num = global_sequence_num + 1; } else if( tag == intrinsic_tag ) { @@ -312,7 +315,9 @@ namespace eosio { namespace chain { my->intrinsic_counter = 0; } - void intrinsic_debug_log::start_action( uint64_t global_sequence_num ) { + void intrinsic_debug_log::start_action( uint64_t global_sequence_num, + name receiver, name first_receiver, name action_name ) + { FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, "invalid operation in read-only mode" ); FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_transaction @@ -320,7 +325,10 @@ namespace eosio { namespace chain { "cannot start action in the current state" ); my->log.put( detail::intrinsic_debug_log_impl::action_tag ); - my->log.write( (char*)&global_sequence_num, sizeof(global_sequence_num) ); + my->log.write( (char*)&global_sequence_num, sizeof(global_sequence_num) ); + my->log.write( (char*)&receiver.value, sizeof(receiver.value) ); + my->log.write( (char*)&first_receiver.value, sizeof(first_receiver.value) ); + my->log.write( (char*)&action_name.value, sizeof(action_name.value) ); my->state = detail::intrinsic_debug_log_impl::state_t::in_action; my->intrinsic_counter = 0; diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index fb935ae7455..9eb73b778d9 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -19,11 +19,13 @@ BOOST_AUTO_TEST_CASE(basic_test) { auto log_path = tempdir.path() / "intrinsic.log"; { auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + name alice("alice"); + name foo("foo"); intrinsic_debug_log log( log_path ); log.open(); log.start_block( 2u ); log.start_transaction( transaction_id_type() ); - log.start_action( 1ull ); + log.start_action( 1ull, alice, alice, foo ); log.acknowledge_intrinsic_without_recording(); log.record_intrinsic( digest, digest ); log.finish_block(); @@ -42,7 +44,11 @@ BOOST_AUTO_TEST_CASE(basic_test) { const char* expected = "00" "02000000" // start of block 2 "01" "0000000000000000000000000000000000000000000000000000000000000000" // start of transaction - "02" "0100000000000000" // start of action with global sequence number of 1 + "02" // start of action with: + "0100000000000000" // global sequence number of 1, + "0000000000855c34" // receiver "alice", + "0000000000855c34" // first_receiver "alice", + "000000000000285d" // and action_name "foo". "03" // recording an intrinsic with: "01000000" // an ordinal of 1, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" // this arguments hash, @@ -57,22 +63,26 @@ BOOST_AUTO_TEST_CASE(iterate_test) { fc::temp_directory tempdir; auto log_path = tempdir.path() / "intrinsic.log"; auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + name alice("alice"); + name bob("bob"); + name foo("foo"); + name bar("bar"); intrinsic_debug_log log( log_path ); log.open(); log.start_block( 2u ); log.start_transaction( transaction_id_type() ); - log.start_action( 1ull ); + log.start_action( 1ull, alice, alice, foo ); log.acknowledge_intrinsic_without_recording(); log.record_intrinsic( digest, digest ); log.finish_block(); log.start_block( 4u ); log.start_transaction( transaction_id_type() ); log.start_transaction( transaction_id_type() ); - log.start_action( 2ull ); + log.start_action( 2ull, alice, alice, bar ); log.record_intrinsic( digest, digest ); log.record_intrinsic( digest, digest ); - log.start_action( 3ull ); + log.start_action( 3ull, bob, alice, bar ); log.finish_block(); log.close(); From 2cebe4376a98063a461662fb8689f596ac800f7b Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 18 May 2019 00:44:38 -0400 Subject: [PATCH 18/88] add intrinsic_debug_log::find_first_difference --- .../eosio/chain/intrinsic_debug_log.hpp | 38 +++++- libraries/chain/intrinsic_debug_log.cpp | 118 ++++++++++++++++++ unittests/intrinsic_debug_log_tests.cpp | 63 ++++++++++ 3 files changed, 216 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index 0d30c0b1f80..5ccdb486eac 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -50,6 +50,15 @@ namespace eosio { namespace chain { uint32_t intrinsic_ordinal = 0; digest_type arguments_hash; digest_type memory_hash; + + friend bool operator == ( const intrinsic_record& lhs, const intrinsic_record& rhs ) { + return std::tie( lhs.intrinsic_ordinal, lhs.arguments_hash, lhs.memory_hash ) + == std::tie( rhs.intrinsic_ordinal, rhs.arguments_hash, rhs.memory_hash ); + } + + friend bool operator != ( const intrinsic_record& lhs, const intrinsic_record& rhs ) { + return !(lhs == rhs); + } }; struct action_data { @@ -57,7 +66,7 @@ namespace eosio { namespace chain { name receiver{}; name first_receiver{}; name action_name{}; - std::vector< intrinsic_record > recorded_intrinsics; + std::vector recorded_intrinsics; }; struct transaction_data { @@ -67,7 +76,7 @@ namespace eosio { namespace chain { struct block_data { uint32_t block_num = 0; - std::vector< transaction_data > transactions; + std::vector transactions; }; class block_iterator : public std::iterator { @@ -130,6 +139,24 @@ namespace eosio { namespace chain { block_reverse_iterator rbegin_block()const { return std::make_reverse_iterator( begin_block() ); } block_reverse_iterator rend_blocks()const { return std::make_reverse_iterator( end_block() ); } + struct intrinsic_differences { + uint32_t block_num = 0; + transaction_id_type trx_id; + uint64_t global_sequence_num = 0; + name receiver{}; + name first_receiver{}; + name action_name{}; + std::vector lhs_recorded_intrinsics; + std::vector rhs_recorded_intrinsics; + }; + + /** + * @pre requires both lhs and rhs to both have the same blocks, transactions, and actions (otherwise it throws) + * @return empty optional if no difference; otherwise the intrinsic_differences for the first different action + */ + static std::optional + find_first_difference( intrinsic_debug_log& lhs, intrinsic_debug_log& rhs ); + private: std::unique_ptr my; }; @@ -138,6 +165,11 @@ namespace eosio { namespace chain { FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_record, (intrinsic_ordinal)(arguments_hash)(memory_hash) ) FC_REFLECT( eosio::chain::intrinsic_debug_log::action_data, - (global_sequence_num)(receiver)(first_receiver)(action_name)(recorded_intrinsics) ) + (global_sequence_num)(receiver)(first_receiver)(action_name)(recorded_intrinsics) +) FC_REFLECT( eosio::chain::intrinsic_debug_log::transaction_data, (trx_id)(actions) ) FC_REFLECT( eosio::chain::intrinsic_debug_log::block_data, (block_num)(transactions) ) +FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_differences, + (block_num)(trx_id)(global_sequence_num)(receiver)(first_receiver)(action_name) + (lhs_recorded_intrinsics)(rhs_recorded_intrinsics) +) diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 56a427e600d..68a583157fe 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -422,4 +423,121 @@ namespace eosio { namespace chain { return *this; } + std::optional + intrinsic_debug_log::find_first_difference( intrinsic_debug_log& lhs, intrinsic_debug_log& rhs ) { + lhs.close(); + rhs.close(); + lhs.open( intrinsic_debug_log::open_mode::read_only ); + rhs.open( intrinsic_debug_log::open_mode::read_only ); + + auto close_logs = fc::make_scoped_exit([&lhs, &rhs] { + lhs.close(); + rhs.close(); + }); + + enum class mismatch_t { + equivalent, + lhs_ended_early, + rhs_ended_early, + mismatch + }; + + auto find_if_mismatched = []( auto&& lhs_begin, auto&& lhs_end, + auto&& rhs_begin, auto&& rhs_end, + auto&& compare_equivalent ) -> mismatch_t + { + for( ; lhs_begin != lhs_end && rhs_begin != rhs_end; ++lhs_begin, ++rhs_begin ) { + const auto& lhs_value = *lhs_begin; + const auto& rhs_value = *rhs_begin; + if( compare_equivalent( lhs_value, rhs_value ) ) continue; + return mismatch_t::mismatch; + } + + bool lhs_empty = (lhs_begin == lhs_end); + bool rhs_empty = (rhs_begin == rhs_end); + // lhs_empty and rhs_empty cannot both be false at this point since we exited the for loop. + + if( lhs_empty && !rhs_empty ) return mismatch_t::lhs_ended_early; + if( !lhs_empty && rhs_empty ) return mismatch_t::rhs_ended_early; + return mismatch_t::equivalent; + }; + + std::optional result; + const char* error_msg = nullptr; + + auto status = find_if_mismatched( + lhs.begin_block(), rhs.end_block(), + rhs.begin_block(), rhs.end_block(), + [&find_if_mismatched, &result, &error_msg] + ( const block_data& block_lhs, const block_data& block_rhs ) { + if( block_lhs.block_num != block_rhs.block_num ) { + error_msg = "logs do not have the same blocks"; + return false; + } + auto status2 = find_if_mismatched( + block_lhs.transactions.begin(), block_lhs.transactions.end(), + block_rhs.transactions.begin(), block_rhs.transactions.end(), + [&find_if_mismatched, &result, &error_msg, &block_lhs] + ( const transaction_data& trx_lhs, const transaction_data& trx_rhs ) { + if( trx_lhs.trx_id != trx_rhs.trx_id ) { + return false; + } + auto status3 = find_if_mismatched( + trx_lhs.actions.begin(), trx_lhs.actions.end(), + trx_rhs.actions.begin(), trx_rhs.actions.end(), + [&result, &block_lhs, &trx_lhs] + ( const action_data& act_lhs, const action_data& act_rhs ) { + if( std::tie( act_lhs.global_sequence_num, + act_lhs.receiver, + act_lhs.first_receiver, + act_lhs.action_name ) + != std::tie( act_rhs.global_sequence_num, + act_rhs.receiver, + act_rhs.first_receiver, + act_rhs.action_name ) + ) { + return false; + } + if( act_lhs.recorded_intrinsics != act_rhs.recorded_intrinsics ) { + result.emplace( intrinsic_differences{ + .block_num = block_lhs.block_num, + .trx_id = trx_lhs.trx_id, + .global_sequence_num = act_lhs.global_sequence_num, + .receiver = act_lhs.receiver, + .first_receiver = act_lhs.first_receiver, + .action_name = act_lhs.action_name, + .lhs_recorded_intrinsics = act_lhs.recorded_intrinsics, + .rhs_recorded_intrinsics = act_rhs.recorded_intrinsics + } ); + return false; + } + return true; + } + ); + if( status3 == mismatch_t::equivalent ) return true; + if( error_msg == nullptr && !result ) { + error_msg = "different actions within a particular transaction"; + } + return false; + } + ); + if( status2 == mismatch_t::equivalent ) return true; + if( error_msg == nullptr && !result ) { + error_msg = "different transactions in a particular block"; + } + return false; + } + ); + + if( status == mismatch_t::mismatch ) { + if( error_msg != nullptr ) { + FC_THROW_EXCEPTION( fc::exception, error_msg ); + } + return result; + } + + FC_ASSERT( status == mismatch_t::equivalent, "logs do not have the same blocks" ); + return {}; // the logs are equivalent + } + } } /// eosio::chain diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index 9eb73b778d9..05e20b527c7 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -130,4 +130,67 @@ BOOST_AUTO_TEST_CASE(iterate_test) { } } +BOOST_AUTO_TEST_CASE(difference_test) { + fc::temp_directory tempdir; + auto log1_path = tempdir.path() / "intrinsic1.log"; + auto log2_path = tempdir.path() / "intrinsic2.log"; + auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + name alice("alice"); + name bob("bob"); + name foo("foo"); + name bar("bar"); + + intrinsic_debug_log log1( log1_path ); + { + log1.open(); + log1.start_block( 2u ); + log1.start_transaction( transaction_id_type() ); + log1.start_action( 1ull, alice, alice, foo ); + log1.acknowledge_intrinsic_without_recording(); + log1.record_intrinsic( digest, digest ); + log1.finish_block(); + log1.start_block( 4u ); + log1.start_transaction( trx_id ); + log1.start_action( 2ull, alice, alice, bar ); + log1.record_intrinsic( digest, digest ); + log1.record_intrinsic( digest, digest ); + log1.start_action( 3ull, bob, alice, bar ); + log1.finish_block(); + log1.close(); + } + + intrinsic_debug_log log2( log2_path ); + { + log2.open(); + log2.start_block( 2u ); + log2.start_transaction( transaction_id_type() ); + log2.start_action( 1ull, alice, alice, foo ); + log2.acknowledge_intrinsic_without_recording(); + log2.record_intrinsic( digest, digest ); + log2.finish_block(); + log2.start_block( 4u ); + log2.start_transaction( trx_id ); + log2.start_action( 2ull, alice, alice, bar ); + log2.record_intrinsic( digest, digest ); + log2.start_action( 3ull, bob, alice, bar ); + log2.finish_block(); + log2.close(); + } + + auto result = intrinsic_debug_log::find_first_difference( log1, log2 ); + BOOST_REQUIRE( result.has_value() ); + + wdump( (fc::json::to_pretty_string(*result)) ); + + BOOST_CHECK_EQUAL( result->block_num, 4u ); + BOOST_CHECK_EQUAL( result->trx_id, trx_id ); + BOOST_CHECK_EQUAL( result->global_sequence_num, 2ull ); + BOOST_CHECK_EQUAL( result->receiver.to_string(), "alice" ); + BOOST_CHECK_EQUAL( result->first_receiver.to_string(), "alice" ); + BOOST_CHECK_EQUAL( result->action_name.to_string(), "bar" ); + BOOST_CHECK_EQUAL( result->lhs_recorded_intrinsics.size(), 2 ); + BOOST_CHECK_EQUAL( result->rhs_recorded_intrinsics.size(), 1 ); +} + BOOST_AUTO_TEST_SUITE_END() From 83b96c461b4527fd384dd06727c55ce329f7206d Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 18 May 2019 12:00:47 -0400 Subject: [PATCH 19/88] initial integration of intrinsic_debug_log into controller Added debug-log-intrinsics option to chain_plugin which, when enabled, will create a intrinsics.log file in the data directory that logs information of replayed blocks only. Currently, deferred transaction logging is not fully supported. Also no intrinsics records are yet logged. --- libraries/chain/apply_context.cpp | 6 ++ libraries/chain/controller.cpp | 58 +++++++++++++++++++ .../include/eosio/chain/apply_context.hpp | 7 ++- .../chain/include/eosio/chain/controller.hpp | 3 + .../eosio/chain/intrinsic_debug_log.hpp | 3 + libraries/chain/intrinsic_debug_log.cpp | 14 ++++- plugins/chain_plugin/chain_plugin.cpp | 6 ++ 7 files changed, 93 insertions(+), 4 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index dc7687cf05c..ae3d6a09097 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -34,6 +34,7 @@ apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint :control(con) ,db(con.mutable_db()) ,trx_context(trx_ctx) +,intrinsic_log(con.get_intrinsic_debug_log()) ,recurse_depth(depth) ,first_receiver_action_ordinal(action_ordinal) ,action_ordinal(action_ordinal) @@ -53,6 +54,11 @@ void apply_context::exec_one() { auto start = fc::time_point::now(); + if( intrinsic_log ) { + intrinsic_log->start_action( control.get_dynamic_global_properties().global_action_sequence + 1, + receiver, act->account, act->name ); + } + action_receipt r; r.receiver = receiver; r.act_digest = digest_type::hash(*act); diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 8f2c2f2368d..349ea16c43f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -211,6 +212,7 @@ struct controller_impl { chainbase::database db; chainbase::database reversible_blocks; ///< a special database to persist blocks that have successfully been applied but are still reversible block_log blog; + optional intrinsic_log; optional pending; block_state_ptr head; fork_database fork_db; @@ -292,6 +294,7 @@ struct controller_impl { cfg.read_only ? database::read_only : database::read_write, cfg.reversible_cache_size, false, cfg.db_map_mode, cfg.db_hugepage_paths ), blog( cfg.blocks_dir ), + intrinsic_log( cfg.intrinsic_debug_log_path ? *cfg.intrinsic_debug_log_path : optional() ), fork_db( cfg.state_dir ), wasmif( cfg.wasm_runtime, db ), resource_limits( db ), @@ -309,6 +312,10 @@ struct controller_impl { { check_protocol_features( timestamp, cur_features, new_features ); } ); + if( intrinsic_log ) { + intrinsic_log->open(); + } + set_activation_handler(); set_activation_handler(); set_activation_handler(); @@ -594,6 +601,15 @@ struct controller_impl { db.undo(); } + if( intrinsic_log && intrinsic_log->last_block_num() > 0 && head->block_num != intrinsic_log->last_block_num() ) { + wlog( "head block num (${head}) does not match last block in intrinsic.log (${last}): replacing intrinsic.log", + ("head", head->block_num)("last", intrinsic_log->last_block_num()) + ); + intrinsic_log->close(); + fc::remove( intrinsic_log->get_path() ); + intrinsic_log->open(); + } + protocol_features.init( db ); const auto& rbi = reversible_blocks.get_index(); @@ -663,6 +679,22 @@ struct controller_impl { replay( shutdown ); // replay any irreversible and reversible blocks ahead of current head } + if( intrinsic_log ) { + intrinsic_log->close(); + + /* + // Print intrinsic_log in JSON form + intrinsic_log->open( intrinsic_debug_log::open_mode::read_only ); + const auto end_itr = intrinsic_log->end_block(); + for( auto itr = intrinsic_log->begin_block(); itr != end_itr; ++itr ) { + dlog( "\n${json}", ("json", fc::json::to_pretty_string(*itr)) ); + } + intrinsic_log->close(); + */ + + intrinsic_log.reset(); + } + if( shutdown() ) return; if( read_mode != db_read_mode::IRREVERSIBLE @@ -1084,6 +1116,9 @@ struct controller_impl { transaction_trace_ptr trace; if( gtrx.expiration < self.pending_block_time() ) { + if( intrinsic_log ) { + intrinsic_log->start_transaction( gtrx.trx_id ); + } trace = std::make_shared(); trace->id = gtrx.trx_id; trace->block_num = self.head_block_num() + 1; @@ -1105,6 +1140,9 @@ struct controller_impl { uint32_t cpu_time_to_bill_us = billed_cpu_time_us; + // TODO: Figure out how to handle deferred transactions. + // It seems like I need to augment intrinsic_debug_log to support transaction level rollback. + transaction_context trx_context( self, dtrx, gtrx.trx_id ); trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource trx_context.deadline = deadline; @@ -1194,6 +1232,10 @@ struct controller_impl { if ( !subjective ) { // hard failure logic + if( intrinsic_log ) { + intrinsic_log->start_transaction( gtrx.trx_id ); + } + if( !explicit_billed_cpu_time ) { auto& rl = self.get_mutable_resource_limits_manager(); rl.update_account_usage( trx_context.bill_to_accounts, block_timestamp_type(self.pending_block_time()).slot ); @@ -1274,6 +1316,9 @@ struct controller_impl { const signed_transaction& trn = trx->packed_trx()->get_signed_transaction(); transaction_context trx_context(self, trn, trx->id(), start); + if( intrinsic_log ) { + intrinsic_log->start_transaction( trx_context.id ); + } if ((bool)subjective_cpu_leeway && pending->_block_status == controller::block_status::incomplete) { trx_context.leeway = *subjective_cpu_leeway; } @@ -1390,6 +1435,10 @@ struct controller_impl { pending.emplace( maybe_session(), *head, when, confirm_block_count, new_protocol_feature_activations ); } + if( intrinsic_log ) { + intrinsic_log->start_block( head->block_num + 1 ); + } + pending->_block_status = s; pending->_producer_block_id = producer_block_id; @@ -1521,6 +1570,10 @@ struct controller_impl { try { + if( intrinsic_log ) { + intrinsic_log->finish_block(); + } + auto& pbhs = pending->get_pending_block_header_state(); // Update resource limits: @@ -2224,6 +2277,11 @@ const protocol_feature_manager& controller::get_protocol_feature_manager()const return my->protocol_features; } +optional& controller::get_intrinsic_debug_log() +{ + return my->intrinsic_log; +} + controller::controller( const controller::config& cfg ) :my( new controller_impl( cfg, *this, protocol_feature_set{} ) ) { diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index ce7ae2bdae7..87af8500f82 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -559,9 +559,10 @@ class apply_context { /// Fields: public: - controller& control; - chainbase::database& db; ///< database where state is stored - transaction_context& trx_context; ///< transaction context in which the action is running + controller& control; + chainbase::database& db; ///< database where state is stored + transaction_context& trx_context; ///< transaction context in which the action is running + optional& intrinsic_log; private: const action* act = nullptr; ///< action being applied diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index c7702d09414..9b288c1d1f2 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace chainbase { class database; @@ -77,6 +78,7 @@ namespace eosio { namespace chain { bool contracts_console = false; bool allow_ram_billing_in_notify = false; bool disable_all_subjective_mitigations = false; //< for testing purposes only + optional intrinsic_debug_log_path; //< for debugging purposes only genesis_state genesis; wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime; @@ -173,6 +175,7 @@ namespace eosio { namespace chain { const authorization_manager& get_authorization_manager()const; authorization_manager& get_mutable_authorization_manager(); const protocol_feature_manager& get_protocol_feature_manager()const; + optional& get_intrinsic_debug_log(); const flat_set& get_actor_whitelist() const; const flat_set& get_actor_blacklist() const; diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index 5ccdb486eac..65a29a456db 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -139,6 +139,9 @@ namespace eosio { namespace chain { block_reverse_iterator rbegin_block()const { return std::make_reverse_iterator( begin_block() ); } block_reverse_iterator rend_blocks()const { return std::make_reverse_iterator( end_block() ); } + const boost::filesystem::path& get_path()const; + uint32_t last_block_num()const; + struct intrinsic_differences { uint32_t block_num = 0; transaction_id_type trx_id; diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 68a583157fe..2b10c4fd27d 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -115,9 +115,12 @@ namespace eosio { namespace chain { bfs::resize_file( log_path, last_commited_block_pos ); } state = state_t::closed; + file_size = -1; + last_commited_block_pos = 0; + most_recent_block_pos = 0; + most_recent_block_num = 0; intrinsic_counter = 0; written_start_block = false; - file_size = -1; block_data_cache.clear(); } @@ -362,6 +365,15 @@ namespace eosio { namespace chain { ++(my->intrinsic_counter); } + const boost::filesystem::path& intrinsic_debug_log::get_path()const { + return my->log_path; + } + + uint32_t intrinsic_debug_log::last_block_num()const { + FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::closed, "log is closed" ); + return my->most_recent_block_num; + } + intrinsic_debug_log::block_iterator intrinsic_debug_log::begin_block()const { FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, "block_begin is only allowed in read-only mode" ); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 3d0bac826b9..f097d7adc66 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -266,6 +266,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip #ifdef __linux__ ("database-hugepage-path", bpo::value>()->composing(), "Optional path for database hugepages when in \"locked\" mode (may specify multiple times)") #endif + ("debug-log-intrinsics", bpo::bool_switch()->default_value(false), + "enable logging of intrinsics for debugging purposes") ; // TODO: rate limiting @@ -602,6 +604,10 @@ void chain_plugin::plugin_initialize(const variables_map& options) { my->blocks_dir = bld; } + if( options.at( "debug-log-intrinsics" ).as() ) { + my->chain_config->intrinsic_debug_log_path = app().data_dir() / "intrinsics.log"; + } + protocol_feature_set pfs; { fc::path protocol_features_dir; From 5f7298e099b5282f982ad146d6e804b4225ad7fb Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 18 May 2019 12:02:45 -0400 Subject: [PATCH 20/88] bug fixes in intrinsic_debug_log and new unit test --- libraries/chain/intrinsic_debug_log.cpp | 77 ++++++++++++++++++++----- unittests/intrinsic_debug_log_tests.cpp | 71 +++++++++++++++++++++++ 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 2b10c4fd27d..f56449f7a8e 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -51,7 +51,7 @@ namespace eosio { namespace chain { std::map block_data_cache; state_t state = state_t::closed; int64_t file_size = -1; - uint64_t last_commited_block_pos = 0; + uint64_t last_committed_block_pos = 0; uint64_t most_recent_block_pos = 0; uint32_t most_recent_block_num = 0; uint32_t intrinsic_counter = 0; @@ -60,6 +60,8 @@ namespace eosio { namespace chain { void open( bool read_only, bool start_fresh ); void close(); + void truncate_to( uint64_t pos ); + void start_block( uint32_t block_num ); void ensure_block_start_written(); void finish_block(); @@ -90,19 +92,24 @@ namespace eosio { namespace chain { log.seekg( 0, std::ios::end ); file_size = log.tellg(); if( file_size > 0 ) { - log.seekg( -sizeof(last_commited_block_pos), std::ios::end ); - log.read( (char*)&last_commited_block_pos, sizeof(last_commited_block_pos) ); - FC_ASSERT( last_commited_block_pos < file_size, "corrupted log: invalid block position" ); - most_recent_block_pos = last_commited_block_pos; + log.seekg( -sizeof(last_committed_block_pos), std::ios::end ); + log.read( (char*)&last_committed_block_pos, sizeof(last_committed_block_pos) ); + FC_ASSERT( last_committed_block_pos < file_size, "corrupted log: invalid block position" ); + most_recent_block_pos = last_committed_block_pos; - log.seekg( last_commited_block_pos, std::ios::beg ); + log.seekg( last_committed_block_pos, std::ios::beg ); uint8_t tag = 0; log.read( (char*)&tag, sizeof(tag) ); FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); log.read( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); } - log.seekg( 0, std::ios::beg ); + + if( read_only ) { + log.seekg( 0, std::ios::beg ); + } else { + log.seekp( 0, std::ios::end ); + } } void intrinsic_debug_log_impl::close() { @@ -112,11 +119,11 @@ namespace eosio { namespace chain { log.flush(); log.close(); if( state != state_t::read_only && state != state_t::waiting_to_start_block ) { - bfs::resize_file( log_path, last_commited_block_pos ); + bfs::resize_file( log_path, most_recent_block_pos ); } state = state_t::closed; file_size = -1; - last_commited_block_pos = 0; + last_committed_block_pos = 0; most_recent_block_pos = 0; most_recent_block_num = 0; intrinsic_counter = 0; @@ -125,13 +132,34 @@ namespace eosio { namespace chain { block_data_cache.clear(); } + void intrinsic_debug_log_impl::truncate_to( uint64_t pos ) { + FC_ASSERT( state != state_t::closed && state != state_t::read_only, "called while in invalid state" ); + FC_ASSERT( pos >= most_recent_block_pos, "truncation would violate invariants" ); + + log.seekp( 0, std::ios::end ); + file_size = log.tellp(); + FC_ASSERT( pos <= file_size, "position is out of bounds" ); + + log.flush(); + if( pos == file_size ) return; + + if( log.tellg() > pos ) { + log.seekg( pos ); + } + + bfs::resize_file( log_path, pos ); + + log.sync(); + } + + void intrinsic_debug_log_impl::start_block( uint32_t block_num ) { FC_ASSERT( state == state_t::waiting_to_start_block, "cannot start block while still processing a block" ); FC_ASSERT( most_recent_block_num < block_num, "must start a block with greater height than previously started blocks" ); state = state_t::in_block; - most_recent_block_pos = log.tellg(); + most_recent_block_pos = log.tellp(); most_recent_block_num = block_num; } @@ -147,6 +175,23 @@ namespace eosio { namespace chain { if( written_start_block ) { log.put( block_tag ); log.write( (char*)&most_recent_block_pos, sizeof(most_recent_block_pos) ); + file_size = log.tellp(); + last_committed_block_pos = most_recent_block_pos; + log.flush(); + } else { + most_recent_block_pos = last_committed_block_pos; + if( file_size > 0 ) { + auto old_pos = log.tellg(); + log.seekg( most_recent_block_pos, std::ios::beg ); + uint8_t tag = 0; + log.read( (char*)&tag, sizeof(tag) ); + FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); + + log.read( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); + log.seekg( old_pos, std::ios::beg ); + } else { + most_recent_block_num = 0; + } } state = state_t::waiting_to_start_block; intrinsic_counter = 0; @@ -155,8 +200,8 @@ namespace eosio { namespace chain { int64_t intrinsic_debug_log_impl::get_position_of_previous_block( int64_t pos ) { int64_t previous_pos = -1; - if( pos > sizeof(last_commited_block_pos) ) { - decltype(last_commited_block_pos) read_pos{}; + if( pos > sizeof(last_committed_block_pos) ) { + decltype(last_committed_block_pos) read_pos{}; log.seekg( pos - sizeof(read_pos), std::ios::beg ); log.read( (char*)&read_pos, sizeof(read_pos) ); FC_ASSERT( read_pos < log.tellg(), "corrupted log: invalid block position" ); @@ -191,7 +236,7 @@ namespace eosio { namespace chain { for(;;) { log.read( (char*)&tag, sizeof(tag) ); if( tag == block_tag ) { - decltype(last_commited_block_pos) read_pos{}; + decltype(last_committed_block_pos) read_pos{}; log.read( (char*)&read_pos, sizeof(read_pos) ); FC_ASSERT( read_pos == pos, "corrupted log: block position did not match" ); break; @@ -377,7 +422,7 @@ namespace eosio { namespace chain { intrinsic_debug_log::block_iterator intrinsic_debug_log::begin_block()const { FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, "block_begin is only allowed in read-only mode" ); - return block_iterator( my.get(), (my->last_commited_block_pos == 0ull ? -1ll : 0ll) ); + return block_iterator( my.get(), (my->file_size > 0 ? 0ll : -1ll) ); } intrinsic_debug_log::block_iterator intrinsic_debug_log::end_block()const { @@ -418,7 +463,7 @@ namespace eosio { namespace chain { if( _pos < 0 ) { FC_ASSERT( !_cached_block_data, "invariant failure" ); FC_ASSERT( _log->file_size > 0, "cannot decrement end block iterator when the log contains no blocks" ); - _pos = static_cast( _log->last_commited_block_pos ); + _pos = static_cast( _log->last_committed_block_pos ); return *this; } @@ -478,7 +523,7 @@ namespace eosio { namespace chain { const char* error_msg = nullptr; auto status = find_if_mismatched( - lhs.begin_block(), rhs.end_block(), + lhs.begin_block(), lhs.end_block(), rhs.begin_block(), rhs.end_block(), [&find_if_mismatched, &result, &error_msg] ( const block_data& block_lhs, const block_data& block_rhs ) { diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index 05e20b527c7..bfe501a483a 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -69,6 +69,13 @@ BOOST_AUTO_TEST_CASE(iterate_test) { name bar("bar"); intrinsic_debug_log log( log_path ); + log.open(); + log.close(); + + log.open( intrinsic_debug_log::open_mode::read_only ); + BOOST_CHECK( log.begin_block() == log.end_block() ); + log.close(); + log.open(); log.start_block( 2u ); log.start_transaction( transaction_id_type() ); @@ -76,6 +83,13 @@ BOOST_AUTO_TEST_CASE(iterate_test) { log.acknowledge_intrinsic_without_recording(); log.record_intrinsic( digest, digest ); log.finish_block(); + log.close(); + + log.open( intrinsic_debug_log::open_mode::read_only ); + BOOST_CHECK( log.begin_block() != log.end_block() ); + log.close(); + + log.open(); log.start_block( 4u ); log.start_transaction( transaction_id_type() ); log.start_transaction( transaction_id_type() ); @@ -193,4 +207,61 @@ BOOST_AUTO_TEST_CASE(difference_test) { BOOST_CHECK_EQUAL( result->rhs_recorded_intrinsics.size(), 1 ); } +BOOST_AUTO_TEST_CASE(equivalence_test) { + fc::temp_directory tempdir; + auto log1_path = tempdir.path() / "intrinsic1.log"; + auto log2_path = tempdir.path() / "intrinsic2.log"; + auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + name alice("alice"); + name bob("bob"); + name foo("foo"); + name bar("bar"); + + intrinsic_debug_log log1( log1_path ); + { + log1.open(); + log1.start_block( 1u ); + log1.finish_block(); + log1.start_block( 2u ); + log1.start_transaction( transaction_id_type() ); + log1.start_action( 1ull, alice, alice, foo ); + log1.acknowledge_intrinsic_without_recording(); + log1.record_intrinsic( digest, digest ); + log1.finish_block(); + log1.start_block( 4u ); + log1.start_transaction( trx_id ); + log1.start_action( 2ull, alice, alice, bar ); + log1.record_intrinsic( digest, digest ); + log1.start_action( 3ull, bob, alice, bar ); + log1.finish_block(); + log1.start_block( 5u ); + log1.close(); + } + + intrinsic_debug_log log2( log2_path ); + { + log2.open(); + log2.start_block( 2u ); + log2.start_transaction( transaction_id_type() ); + log2.start_action( 1ull, alice, alice, foo ); + log2.acknowledge_intrinsic_without_recording(); + log2.record_intrinsic( digest, digest ); + log2.finish_block(); + log2.start_block( 4u ); + log2.start_transaction( trx_id ); + log2.start_action( 2ull, alice, alice, bar ); + log2.record_intrinsic( digest, digest ); + log2.start_action( 3ull, bob, alice, bar ); + log2.finish_block(); + log2.close(); + } + + auto result = intrinsic_debug_log::find_first_difference( log1, log2 ); + if( result ) { + wdump( (fc::json::to_pretty_string(*result)) ); + } + BOOST_REQUIRE( !result ); +} + BOOST_AUTO_TEST_SUITE_END() From 43ae5f9ef8d476016f9b966a27f86f127262661c Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 18 May 2019 18:48:07 -0400 Subject: [PATCH 21/88] refactor intrinsic_debug_log, fix bugs, and add ability to abort pending block or transaction tracked by the log --- libraries/chain/controller.cpp | 6 +- .../eosio/chain/intrinsic_debug_log.hpp | 9 +- libraries/chain/intrinsic_debug_log.cpp | 753 +++++++++++++----- unittests/intrinsic_debug_log_tests.cpp | 6 + 4 files changed, 558 insertions(+), 216 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 349ea16c43f..55a7ceab1a7 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -601,9 +601,11 @@ struct controller_impl { db.undo(); } - if( intrinsic_log && intrinsic_log->last_block_num() > 0 && head->block_num != intrinsic_log->last_block_num() ) { + if( intrinsic_log && intrinsic_log->last_committed_block_num() > 0 + && head->block_num != intrinsic_log->last_committed_block_num() ) + { wlog( "head block num (${head}) does not match last block in intrinsic.log (${last}): replacing intrinsic.log", - ("head", head->block_num)("last", intrinsic_log->last_block_num()) + ("head", head->block_num)("last", intrinsic_log->last_committed_block_num()) ); intrinsic_log->close(); fc::remove( intrinsic_log->get_path() ); diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index 65a29a456db..778dd5c433b 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -39,7 +39,9 @@ namespace eosio { namespace chain { void close(); void start_block( uint32_t block_num ); + void abort_block(); void start_transaction( const transaction_id_type& trx_id ); + void abort_transaction(); void start_action( uint64_t global_sequence_num, name receiver, name first_receiver, name action_name ); void acknowledge_intrinsic_without_recording(); @@ -139,8 +141,13 @@ namespace eosio { namespace chain { block_reverse_iterator rbegin_block()const { return std::make_reverse_iterator( begin_block() ); } block_reverse_iterator rend_blocks()const { return std::make_reverse_iterator( end_block() ); } + bool is_open()const; + bool is_read_only()const; + const boost::filesystem::path& get_path()const; - uint32_t last_block_num()const; + + /** @returns the block number of the last committed block in the log or 0 if no blocks are committed in the log */ + uint32_t last_committed_block_num()const; struct intrinsic_differences { uint32_t block_num = 0; diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index f56449f7a8e..9d181d96042 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -8,16 +8,30 @@ #include #include #include +#include #include namespace bfs = boost::filesystem; +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +using pos_t = uint64_t; + namespace eosio { namespace chain { namespace detail { + + template + struct is_one_of { + static constexpr bool value = (std::is_same_v || ...); + }; + struct extended_block_data { extended_block_data( intrinsic_debug_log::block_data&& block_data, - int64_t pos, int64_t previous_pos, int64_t next_pos ) + const pos_t& pos, + const std::optional& previous_pos, + const std::optional& next_pos ) :block_data( std::move(block_data) ) ,pos( pos ) ,previous_pos( previous_pos ) @@ -25,61 +39,211 @@ namespace eosio { namespace chain { {} intrinsic_debug_log::block_data block_data; - int64_t pos; //< pos should never be negative - int64_t previous_pos; //< if previous_pos is -1, then this is first block in the log - int64_t next_pos; //< if next_pos is -1, then this is the last block in the log + pos_t pos; + std::optional previous_pos; //< if empty, then this is first block in the log + std::optional next_pos; //< if empty, then this is the last block in the log }; class intrinsic_debug_log_impl { public: - enum class state_t { - closed, - read_only, - waiting_to_start_block, - in_block, - in_transaction, - in_action + static constexpr uint8_t block_tag = 0; + static constexpr uint8_t transaction_tag = 1; + static constexpr uint8_t action_tag = 2; + static constexpr uint8_t intrinsic_tag = 3; + + struct closed {}; + + struct read_only { + explicit read_only( uint32_t last_committed_block_num ) + :last_committed_block_num( last_committed_block_num ) + {} + + uint32_t last_committed_block_num; }; - static const uint8_t block_tag = 0; - static const uint8_t transaction_tag = 1; - static const uint8_t action_tag = 2; - static const uint8_t intrinsic_tag = 3; - - std::fstream log; - bfs::path log_path; - std::map block_data_cache; - state_t state = state_t::closed; - int64_t file_size = -1; - uint64_t last_committed_block_pos = 0; - uint64_t most_recent_block_pos = 0; - uint32_t most_recent_block_num = 0; - uint32_t intrinsic_counter = 0; - bool written_start_block = false; - - void open( bool read_only, bool start_fresh ); - void close(); + struct in_block; + struct in_transaction; + struct in_action; - void truncate_to( uint64_t pos ); + struct waiting_to_start_block { + explicit waiting_to_start_block( uint32_t last_committed_block_num ); + explicit waiting_to_start_block( const in_block& s ); + explicit waiting_to_start_block( const in_transaction& s ); + explicit waiting_to_start_block( const in_action& s ); + + uint32_t last_committed_block_num; + }; + + struct in_block { + in_block( const waiting_to_start_block& s, pos_t pos, uint32_t num ); + explicit in_block( const in_transaction& s ); + explicit in_block( const in_action& s ); + + pos_t block_start_pos; + uint32_t block_num; + uint32_t last_committed_block_num; + bool block_header_written = false; + }; + + struct in_transaction { + in_transaction( const in_block& s, pos_t transaction_start_pos ); + in_transaction( const in_transaction& s, pos_t transaction_start_pos ); + in_transaction( const in_action& s, pos_t transaction_start_pos ); + + pos_t block_start_pos; + pos_t transaction_start_pos; + uint32_t block_num; + uint32_t last_committed_block_num; + }; + + struct in_action { + struct force_new_t {}; + + explicit in_action( const in_transaction& s ); + in_action( const in_transaction& s, force_new_t ):in_action( s ){} + in_action( const in_action& s, force_new_t ); + + pos_t block_start_pos; + pos_t transaction_start_pos; + uint32_t block_num; + uint32_t last_committed_block_num; + uint32_t intrinsic_counter = 0; + }; + + using state_type = std::variant< + closed, + read_only, + waiting_to_start_block, + in_block, + in_transaction, + in_action + >; + + struct log_metadata_t { + pos_t last_committed_block_begin_pos{}; + pos_t last_committed_block_end_pos{}; + }; + + std::fstream log; + bfs::path log_path; + std::map block_data_cache; + state_type state{ closed{} }; + std::optional log_metadata; + + void open( bool open_as_read_only, bool start_fresh ); + void close(); void start_block( uint32_t block_num ); - void ensure_block_start_written(); + void abort_block(); + void start_transaction( const transaction_id_type& trx_id ); + void abort_transaction(); + void start_action( uint64_t global_sequence_num, + name receiver, name first_receiver, name action_name ); + void acknowledge_intrinsic_without_recording(); + void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); void finish_block(); - int64_t get_position_of_previous_block( int64_t pos ); - const extended_block_data& get_block_at_position( int64_t pos ); + bool is_open()const { + return !std::holds_alternative( state ); + } + + bool is_read_only()const { + return std::holds_alternative( state ); + } + + bool can_write()const { + return !std::holds_alternative( state ) && !std::holds_alternative( state ); + } + + protected: + void truncate_to( uint64_t pos ); + std::optional get_position_of_previous_block( pos_t pos ); + const extended_block_data& get_block_at_position( pos_t pos ); + + friend class intrinsic_debug_log::block_iterator; }; - void intrinsic_debug_log_impl::open( bool read_only, bool start_fresh ) { + intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( uint32_t last_committed_block_num ) + :last_committed_block_num( last_committed_block_num ) + {} + + intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_block& s ) + :last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_transaction& s ) + :last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_action& s ) + :last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_block::in_block( const waiting_to_start_block& s, pos_t pos, uint32_t num ) + :block_start_pos( pos ) + ,block_num( num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_block::in_block( const in_transaction& s ) + :block_start_pos( s.block_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + ,block_header_written(true) + {} + + intrinsic_debug_log_impl::in_block::in_block( const in_action& s ) + :block_start_pos( s.block_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + ,block_header_written(true) + {} + + intrinsic_debug_log_impl::in_transaction::in_transaction( const in_block& s, pos_t transaction_start_pos ) + :block_start_pos( s.block_start_pos ) + ,transaction_start_pos( transaction_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_transaction::in_transaction( const in_transaction& s, pos_t transaction_start_pos ) + :block_start_pos( s.block_start_pos ) + ,transaction_start_pos( transaction_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_transaction::in_transaction( const in_action& s, pos_t transaction_start_pos ) + :block_start_pos( s.block_start_pos ) + ,transaction_start_pos( transaction_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_action::in_action( const in_transaction& s ) + :block_start_pos( s.block_start_pos ) + ,transaction_start_pos( s.transaction_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + intrinsic_debug_log_impl::in_action::in_action( const in_action& s, in_action::force_new_t ) + :block_start_pos( s.block_start_pos ) + ,transaction_start_pos( s.transaction_start_pos ) + ,block_num( s.block_num ) + ,last_committed_block_num( s.last_committed_block_num ) + {} + + void intrinsic_debug_log_impl::open( bool open_as_read_only, bool start_fresh ) { FC_ASSERT( !log.is_open(), "cannot open when the log is already open" ); - FC_ASSERT( state == state_t::closed, "state out of sync" ); + FC_ASSERT( std::holds_alternative( state ), "state out of sync" ); std::ios_base::openmode open_mode = std::ios::in | std::ios::binary; - if( !read_only ) { - open_mode |= (std::ios::out | std::ios::app); - } else { + if( open_as_read_only ) { FC_ASSERT( !start_fresh, "start_fresh cannot be true in read-only mode" ); + } else { + open_mode |= (std::ios::out | std::ios::app); } if( start_fresh ) { @@ -87,64 +251,89 @@ namespace eosio { namespace chain { } log.open( log_path.generic_string().c_str(), open_mode ); - state = (read_only ? state_t::read_only : state_t::waiting_to_start_block); + + uint32_t last_committed_block_num = 0; // 0 indicates no committed blocks in log log.seekg( 0, std::ios::end ); - file_size = log.tellg(); + pos_t file_size = log.tellg(); if( file_size > 0 ) { - log.seekg( -sizeof(last_committed_block_pos), std::ios::end ); - log.read( (char*)&last_committed_block_pos, sizeof(last_committed_block_pos) ); - FC_ASSERT( last_committed_block_pos < file_size, "corrupted log: invalid block position" ); - most_recent_block_pos = last_committed_block_pos; - - log.seekg( last_committed_block_pos, std::ios::beg ); + pos_t last_committed_block_begin_pos{}; + FC_ASSERT( file_size > sizeof(last_committed_block_begin_pos), + "corrupted log: file size is too small to be valid" ); + log.seekg( -sizeof(last_committed_block_begin_pos), std::ios::end ); + fc::raw::unpack( log, last_committed_block_begin_pos ); + FC_ASSERT( last_committed_block_begin_pos < file_size, "corrupted log: invalid block position" ); + + log.seekg( last_committed_block_begin_pos, std::ios::beg ); uint8_t tag = 0; - log.read( (char*)&tag, sizeof(tag) ); + fc::raw::unpack( log, tag ); FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - log.read( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); + fc::raw::unpack( log, last_committed_block_num ); + + log_metadata.emplace( log_metadata_t{ last_committed_block_begin_pos, file_size } ); } - if( read_only ) { + if( open_as_read_only ) { + state.emplace( last_committed_block_num ); log.seekg( 0, std::ios::beg ); } else { + state.emplace( last_committed_block_num ); log.seekp( 0, std::ios::end ); } } void intrinsic_debug_log_impl::close() { if( !log.is_open() ) return; - FC_ASSERT( state != state_t::closed, "state out of sync" ); - log.flush(); - log.close(); - if( state != state_t::read_only && state != state_t::waiting_to_start_block ) { - bfs::resize_file( log_path, most_recent_block_pos ); + std::optional truncate_pos = std::visit( overloaded{ + []( closed ) -> std::optional { + FC_ASSERT( false, "state out of sync" ); + return {}; // unreachable, only added for compilation purposes + }, + []( const read_only& ) -> std::optional { return {}; }, + []( const waiting_to_start_block& ) -> std::optional { return {}; }, + []( const in_block& ) -> std::optional { return {}; }, + []( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_transaction, in_action + >::value, + std::optional + > + { + return s.block_start_pos; + } + }, state ); + + if( truncate_pos ) { + truncate_to( *truncate_pos ); + } else { + log.flush(); } - state = state_t::closed; - file_size = -1; - last_committed_block_pos = 0; - most_recent_block_pos = 0; - most_recent_block_num = 0; - intrinsic_counter = 0; - written_start_block = false; + log.close(); + + state.emplace(); + log_metadata.reset(); block_data_cache.clear(); } - void intrinsic_debug_log_impl::truncate_to( uint64_t pos ) { - FC_ASSERT( state != state_t::closed && state != state_t::read_only, "called while in invalid state" ); - FC_ASSERT( pos >= most_recent_block_pos, "truncation would violate invariants" ); + void intrinsic_debug_log_impl::truncate_to( pos_t pos ) { + FC_ASSERT( can_write(), "called while in invalid state" ); log.seekp( 0, std::ios::end ); - file_size = log.tellp(); - FC_ASSERT( pos <= file_size, "position is out of bounds" ); + pos_t file_size = log.tellp(); + FC_ASSERT( 0 <= pos && pos <= file_size, "position is out of bounds" ); + + FC_ASSERT( !log_metadata || log_metadata->last_committed_block_end_pos <= pos, + "truncation would erase committed block data which is currently not supported" ); log.flush(); if( pos == file_size ) return; - if( log.tellg() > pos ) { - log.seekg( pos ); + if( log.tellp() > pos ) { + log.seekp( pos ); } bfs::resize_file( log_path, pos ); @@ -152,72 +341,235 @@ namespace eosio { namespace chain { log.sync(); } - void intrinsic_debug_log_impl::start_block( uint32_t block_num ) { - FC_ASSERT( state == state_t::waiting_to_start_block, "cannot start block while still processing a block" ); - FC_ASSERT( most_recent_block_num < block_num, - "must start a block with greater height than previously started blocks" ); + FC_ASSERT( block_num > 0, "block number cannot be 0" ); + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + [&]( const waiting_to_start_block& s ) { + FC_ASSERT( s.last_committed_block_num < block_num, + "must start a block with greater height than previously started blocks" ); + state = in_block( s, log.tellp(), block_num ); + return true; + }, + []( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_block, in_transaction, in_action + >::value, + bool + > + { + FC_ASSERT( false, "cannot start block while still processing a block" ); + return false; // unreachable, only added for compilation purposes + } + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); + } - state = state_t::in_block; - most_recent_block_pos = log.tellp(); - most_recent_block_num = block_num; + void intrinsic_debug_log_impl::abort_block() { + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + []( const waiting_to_start_block& ) { + return true; // nothing to abort + }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_block, in_transaction, in_action + >::value, + bool + > + { + truncate_to( s.block_start_pos ); + state = waiting_to_start_block( s ); + return true; + } + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); } - void intrinsic_debug_log_impl::ensure_block_start_written() { - if( written_start_block ) return; - FC_ASSERT( state == state_t::in_block, "called while in invalid state" ); - log.put( block_tag ); - log.write( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); - written_start_block = true; + void intrinsic_debug_log_impl::start_transaction( const transaction_id_type& trx_id ) { + auto write_transaction_header = [&] { + uint64_t transaction_start_pos = log.tellp(); + fc::raw::pack( log, transaction_tag ); + fc::raw::pack( log, trx_id ); + return transaction_start_pos; + }; + + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + []( const waiting_to_start_block& ) { + FC_ASSERT( false, "cannot start transaction in the current state" ); + return false; // unreachable, only added for compilation purposes + }, + [&]( const in_block& s ) { + if( !s.block_header_written ) { + fc::raw::pack( log, block_tag ); + fc::raw::pack( log, s.block_num ); + } + uint64_t transaction_start_pos = write_transaction_header(); + state = in_transaction( s, transaction_start_pos ); + return true; + }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_transaction, in_action + >::value, + bool + > + { + uint64_t transaction_start_pos = write_transaction_header(); + state = in_transaction( s, transaction_start_pos ); + return true; + } + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); + } + + void intrinsic_debug_log_impl::abort_transaction() { + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + []( const waiting_to_start_block& ) { + FC_ASSERT( false, "cannot abort transaction in the current state" ); + return false; // unreachable, only added for compilation purposes + }, + []( const in_block& s ) { + FC_ASSERT( false, "cannot abort transaction in the current state" ); + return false; // unreachable, only added for compilation purposes + }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_transaction, in_action + >::value, + bool + > + { + truncate_to( s.transaction_start_pos ); + state = in_block( s ); + return true; + } + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); + } + + void intrinsic_debug_log_impl::start_action( uint64_t global_sequence_num, + name receiver, name first_receiver, name action_name ) + { + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + []( const waiting_to_start_block& ) { + FC_ASSERT( false, "cannot start action in the current state" ); + return false; // unreachable, only added for compilation purposes + }, + []( const in_block& ) { + FC_ASSERT( false, "cannot start action in the current state" ); + return false; // unreachable, only added for compilation purposes + }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_transaction, in_action + >::value, + bool + > + { + fc::raw::pack( log, action_tag ); + fc::raw::pack( log, global_sequence_num ); + fc::raw::pack( log, receiver ); + fc::raw::pack( log, first_receiver ); + fc::raw::pack( log, action_name ); + state = in_action( s, in_action::force_new_t{} ); + return true; + } + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); + } + + void intrinsic_debug_log_impl::acknowledge_intrinsic_without_recording() { + FC_ASSERT( can_write(), "log is not setup for writing" ); + in_action& s = std::get( state ); + ++(s.intrinsic_counter); + } + + void intrinsic_debug_log_impl::record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ) { + FC_ASSERT( can_write(), "log is not setup for writing" ); + in_action& s = std::get( state ); + + fc::raw::pack( log, intrinsic_tag ); + fc::raw::pack( log, s.intrinsic_counter ); + fc::raw::pack( log, arguments_hash ); + fc::raw::pack( log, memory_hash ); + + ++(s.intrinsic_counter); } void intrinsic_debug_log_impl::finish_block() { - if( written_start_block ) { - log.put( block_tag ); - log.write( (char*)&most_recent_block_pos, sizeof(most_recent_block_pos) ); - file_size = log.tellp(); - last_committed_block_pos = most_recent_block_pos; - log.flush(); - } else { - most_recent_block_pos = last_committed_block_pos; - if( file_size > 0 ) { - auto old_pos = log.tellg(); - log.seekg( most_recent_block_pos, std::ios::beg ); - uint8_t tag = 0; - log.read( (char*)&tag, sizeof(tag) ); - FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - - log.read( (char*)&most_recent_block_num, sizeof(most_recent_block_num) ); - log.seekg( old_pos, std::ios::beg ); - } else { - most_recent_block_num = 0; + bool writable = std::visit( overloaded{ + []( closed ) { return false; }, + []( const read_only& ) { return false; }, + []( const waiting_to_start_block& ) { + FC_ASSERT( false, "no started block" ); + return false; // unreachable, only added for compilation purposes + }, + [&]( const in_block& s ) { + state = waiting_to_start_block( s.last_committed_block_num ); + return true; // nothing to commit + }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + in_transaction, in_action + >::value, + bool + > + { + fc::raw::pack( log, block_tag ); + fc::raw::pack( log, s.block_start_pos ); + pos_t new_last_committed_block_end_pos = log.tellp(); + log.flush(); + + pos_t new_last_committed_block_begin_pos{}; + if( log_metadata ) { + new_last_committed_block_begin_pos = log_metadata->last_committed_block_end_pos; + } + + log_metadata.emplace( log_metadata_t{ new_last_committed_block_begin_pos, new_last_committed_block_end_pos} ); + + state = waiting_to_start_block( s.last_committed_block_num ); + return true; } - } - state = state_t::waiting_to_start_block; - intrinsic_counter = 0; - written_start_block = false; + }, state ); + FC_ASSERT( writable, "log is not setup for writing" ); } - int64_t intrinsic_debug_log_impl::get_position_of_previous_block( int64_t pos ) { - int64_t previous_pos = -1; - if( pos > sizeof(last_committed_block_pos) ) { - decltype(last_committed_block_pos) read_pos{}; + std::optional intrinsic_debug_log_impl::get_position_of_previous_block( pos_t pos ) { + std::optional previous_pos; + if( pos > sizeof(pos_t) ) { + pos_t read_pos = 0; log.seekg( pos - sizeof(read_pos), std::ios::beg ); - log.read( (char*)&read_pos, sizeof(read_pos) ); + fc::raw::unpack( log, read_pos ); FC_ASSERT( read_pos < log.tellg(), "corrupted log: invalid block position" ); previous_pos = read_pos; } return previous_pos; } - const extended_block_data& intrinsic_debug_log_impl::get_block_at_position( int64_t pos ) { - FC_ASSERT( state == state_t::read_only, "only allowed in read-only mode" ); - FC_ASSERT( 0 <= pos && pos < file_size, "position is out of bounds" ); + const extended_block_data& intrinsic_debug_log_impl::get_block_at_position( pos_t pos ) { + FC_ASSERT( is_read_only(), "only allowed in read-only mode" ); + FC_ASSERT( log_metadata && 0 <= pos && pos < log_metadata->last_committed_block_end_pos, + "position is out of bounds" ); - auto itr = block_data_cache.find( static_cast(pos) ); + auto itr = block_data_cache.find( pos ); if( itr != block_data_cache.end() ) return itr->second; - int64_t previous_pos = get_position_of_previous_block( pos ); + std::optional previous_pos = get_position_of_previous_block( pos ); log.seekg( pos, std::ios::beg ); intrinsic_debug_log::block_data block_data; @@ -225,42 +577,39 @@ namespace eosio { namespace chain { // Read all data for the current block. { uint8_t tag = 0; - log.read( (char*)&tag, sizeof(tag) ); + fc::raw::unpack( log, tag ); FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - log.read( (char*)&block_data.block_num, sizeof(block_data.block_num) ); + fc::raw::unpack( log, block_data.block_num ); bool in_transaction = false; bool in_action = false; uint64_t minimum_accepted_global_sequence_num = 0; for(;;) { - log.read( (char*)&tag, sizeof(tag) ); + fc::raw::unpack( log, tag ); if( tag == block_tag ) { - decltype(last_committed_block_pos) read_pos{}; - log.read( (char*)&read_pos, sizeof(read_pos) ); + pos_t read_pos{}; + fc::raw::unpack( log, read_pos ); FC_ASSERT( read_pos == pos, "corrupted log: block position did not match" ); break; } else if( tag == transaction_tag ) { - char buffer[32]; - log.read( buffer, sizeof(buffer) ); - fc::datastream ds( buffer, sizeof(buffer) ); block_data.transactions.emplace_back(); - fc::raw::unpack( ds, block_data.transactions.back().trx_id ); + fc::raw::unpack( log, block_data.transactions.back().trx_id ); in_transaction = true; in_action = false; } else if( tag == action_tag ) { FC_ASSERT( in_transaction, "corrupted log: no start of transaction before encountering action tag"); uint64_t global_sequence_num = 0; - log.read( (char*)&global_sequence_num, sizeof(global_sequence_num) ); + fc::raw::unpack( log, global_sequence_num ); FC_ASSERT( global_sequence_num >= minimum_accepted_global_sequence_num, "corrupted log: global sequence numbers of actions within a block are not monotonically increasing" ); block_data.transactions.back().actions.emplace_back(); auto& act = block_data.transactions.back().actions.back(); act.global_sequence_num = global_sequence_num; - log.read( (char*)&act.receiver.value, sizeof(act.receiver.value) ); - log.read( (char*)&act.first_receiver.value, sizeof(act.first_receiver.value) ); - log.read( (char*)&act.action_name.value, sizeof(act.action_name.value) ); + fc::raw::unpack( log, act.receiver ); + fc::raw::unpack( log, act.first_receiver ); + fc::raw::unpack( log, act.action_name ); in_action = true; minimum_accepted_global_sequence_num = global_sequence_num + 1; } else if( tag == intrinsic_tag ) { @@ -271,17 +620,14 @@ namespace eosio { namespace chain { minimum_accepted_ordinal = intrinsics.back().intrinsic_ordinal + 1; } uint32_t ordinal = 0; - log.read( (char*)&ordinal, sizeof(ordinal) ); + fc::raw::unpack( log, ordinal ); FC_ASSERT( ordinal >= minimum_accepted_ordinal, "corrupted log: intrinsic ordinals within an action are not monotonically increasing" ); - char buffer[64]; - log.read( buffer, sizeof(buffer) ); - fc::datastream ds( buffer, sizeof(buffer) ); intrinsics.emplace_back(); auto& intrinsic = intrinsics.back(); intrinsic.intrinsic_ordinal = ordinal; - fc::raw::unpack( ds, intrinsic.arguments_hash ); - fc::raw::unpack( ds, intrinsic.memory_hash ); + fc::raw::unpack( log, intrinsic.arguments_hash ); + fc::raw::unpack( log, intrinsic.memory_hash ); } else { FC_ASSERT( false, "corrupted log: unrecognized tag" ); } @@ -290,12 +636,12 @@ namespace eosio { namespace chain { FC_ASSERT( block_data.transactions.size() > 0, "corrupted log: empty block included" ); } - int64_t next_pos = -1; - if( log.tellg() < file_size ) next_pos = log.tellg(); + std::optional next_pos; + if( log.tellg() < log_metadata->last_committed_block_end_pos ) next_pos = log.tellg(); auto res = block_data_cache.emplace( std::piecewise_construct, - std::forward_as_tuple( static_cast(pos) ), + std::forward_as_tuple( pos ), std::forward_as_tuple( std::move(block_data), pos, previous_pos, next_pos ) ); return res.first->second; @@ -314,14 +660,16 @@ namespace eosio { namespace chain { intrinsic_debug_log::~intrinsic_debug_log() { if( my ) { - my->close(); + try { + my->close(); + } FC_LOG_AND_DROP() my.reset(); } } void intrinsic_debug_log::open( open_mode mode ) { - bool read_only = (mode == open_mode::read_only); - my->open( read_only, ( read_only ? false : (mode == open_mode::create_new) ) ); + bool open_as_read_only = (mode == open_mode::read_only); + my->open( open_as_read_only, ( open_as_read_only ? false : (mode == open_mode::create_new) ) ); } void intrinsic_debug_log::close() { @@ -333,101 +681,80 @@ namespace eosio { namespace chain { } void intrinsic_debug_log::start_block( uint32_t block_num ) { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); my->start_block( block_num ); } - void intrinsic_debug_log::finish_block() { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); - my->finish_block(); + void intrinsic_debug_log::abort_block() { + my->abort_block(); } void intrinsic_debug_log::start_transaction( const transaction_id_type& trx_id ) { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::closed - && my->state != detail::intrinsic_debug_log_impl::state_t::waiting_to_start_block, - "cannot start transaction in the current state" ); - - char buffer[32]; - fc::datastream ds( buffer, sizeof(buffer) ); - fc::raw::pack( ds, trx_id ); - FC_ASSERT( !ds.remaining(), "unexpected size for transaction id" ); - - my->ensure_block_start_written(); - my->log.put( detail::intrinsic_debug_log_impl::transaction_tag ); - my->log.write( buffer, sizeof(buffer) ); - - my->state = detail::intrinsic_debug_log_impl::state_t::in_transaction; - my->intrinsic_counter = 0; + my->start_transaction( trx_id ); + } + + void intrinsic_debug_log::abort_transaction() { + my->abort_transaction(); } void intrinsic_debug_log::start_action( uint64_t global_sequence_num, name receiver, name first_receiver, name action_name ) { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); - FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_transaction - || my->state == detail::intrinsic_debug_log_impl::state_t::in_action, - "cannot start action in the current state" ); - - my->log.put( detail::intrinsic_debug_log_impl::action_tag ); - my->log.write( (char*)&global_sequence_num, sizeof(global_sequence_num) ); - my->log.write( (char*)&receiver.value, sizeof(receiver.value) ); - my->log.write( (char*)&first_receiver.value, sizeof(first_receiver.value) ); - my->log.write( (char*)&action_name.value, sizeof(action_name.value) ); - - my->state = detail::intrinsic_debug_log_impl::state_t::in_action; - my->intrinsic_counter = 0; + my->start_action( global_sequence_num, receiver, first_receiver, action_name ); } void intrinsic_debug_log::acknowledge_intrinsic_without_recording() { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); - FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_action, - "can only acknowledge intrinsic within action state" ); - ++(my->intrinsic_counter); + my->acknowledge_intrinsic_without_recording(); } void intrinsic_debug_log::record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ) { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::read_only, - "invalid operation in read-only mode" ); - FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::in_action, - "can only record intrinsic within action state" ); - - char buffer[64]; - fc::datastream ds( buffer, sizeof(buffer) ); - fc::raw::pack( ds, arguments_hash ); - fc::raw::pack( ds, memory_hash ); - FC_ASSERT( !ds.remaining(), "unexpected size for digest" ); - - my->log.put( detail::intrinsic_debug_log_impl::intrinsic_tag ); - my->log.write( (char*)&my->intrinsic_counter, sizeof(my->intrinsic_counter) ); - my->log.write( buffer, sizeof(buffer) ); - - ++(my->intrinsic_counter); + my->record_intrinsic( arguments_hash, memory_hash ); + } + + void intrinsic_debug_log::finish_block() { + my->finish_block(); + } + + bool intrinsic_debug_log::is_open()const { + return my->is_open(); + } + + bool intrinsic_debug_log::is_read_only()const { + return my->is_read_only(); } const boost::filesystem::path& intrinsic_debug_log::get_path()const { return my->log_path; } - uint32_t intrinsic_debug_log::last_block_num()const { - FC_ASSERT( my->state != detail::intrinsic_debug_log_impl::state_t::closed, "log is closed" ); - return my->most_recent_block_num; + uint32_t intrinsic_debug_log::last_committed_block_num()const { + std::optional result = std::visit( overloaded{ + []( detail::intrinsic_debug_log_impl::closed ) -> std::optional { return {}; }, + [&]( auto&& s ) + -> std::enable_if_t< + detail::is_one_of< std::decay_t, + detail::intrinsic_debug_log_impl::read_only, + detail::intrinsic_debug_log_impl::waiting_to_start_block, + detail::intrinsic_debug_log_impl::in_block, + detail::intrinsic_debug_log_impl::in_transaction, + detail::intrinsic_debug_log_impl::in_action + >::value, + std::optional + > + { + return s.last_committed_block_num; + } + }, my->state ); + FC_ASSERT( result, "log is closed" ); + return *result; } intrinsic_debug_log::block_iterator intrinsic_debug_log::begin_block()const { - FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, - "block_begin is only allowed in read-only mode" ); - return block_iterator( my.get(), (my->file_size > 0 ? 0ll : -1ll) ); + FC_ASSERT( my->is_read_only(), "block_begin is only allowed in read-only mode" ); + return block_iterator( my.get(), (my->log_metadata ? 0ll : -1ll) ); } intrinsic_debug_log::block_iterator intrinsic_debug_log::end_block()const { - FC_ASSERT( my->state == detail::intrinsic_debug_log_impl::state_t::read_only, - "block_end is only allowed in read-only mode" ); + FC_ASSERT( my->is_read_only(), "block_end is only allowed in read-only mode" ); return block_iterator( my.get() ); } @@ -448,10 +775,10 @@ namespace eosio { namespace chain { FC_ASSERT( _pos >= 0, "cannot increment end block iterator" ); if( !_cached_block_data ) { - _cached_block_data = &_log->get_block_at_position( _pos ); + _cached_block_data = &_log->get_block_at_position( static_cast(_pos) ); } - _pos = _cached_block_data->next_pos; + _pos = (_cached_block_data->next_pos ? static_cast(*_cached_block_data->next_pos) : -1ll); _cached_block_data = nullptr; return *this; @@ -462,20 +789,20 @@ namespace eosio { namespace chain { if( _pos < 0 ) { FC_ASSERT( !_cached_block_data, "invariant failure" ); - FC_ASSERT( _log->file_size > 0, "cannot decrement end block iterator when the log contains no blocks" ); - _pos = static_cast( _log->last_committed_block_pos ); + FC_ASSERT( _log->log_metadata, "cannot decrement end block iterator when the log contains no blocks" ); + _pos = static_cast( _log->log_metadata->last_committed_block_begin_pos ); return *this; } - int64_t prev_pos = -1; + std::optional prev_pos; if( _cached_block_data ) { prev_pos = _cached_block_data->previous_pos; } else { - prev_pos = _log->get_position_of_previous_block( _pos ); + prev_pos = _log->get_position_of_previous_block( static_cast(_pos) ); } - FC_ASSERT( prev_pos >= 0, "cannot decrement begin block iterator" ); - _pos = prev_pos; + FC_ASSERT( prev_pos, "cannot decrement begin block iterator" ); + _pos = static_cast(*prev_pos); _cached_block_data = nullptr; return *this; } diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index bfe501a483a..9d0261a3c02 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -225,6 +225,8 @@ BOOST_AUTO_TEST_CASE(equivalence_test) { log1.finish_block(); log1.start_block( 2u ); log1.start_transaction( transaction_id_type() ); + log1.abort_transaction(); + log1.start_transaction( transaction_id_type() ); log1.start_action( 1ull, alice, alice, foo ); log1.acknowledge_intrinsic_without_recording(); log1.record_intrinsic( digest, digest ); @@ -236,6 +238,10 @@ BOOST_AUTO_TEST_CASE(equivalence_test) { log1.start_action( 3ull, bob, alice, bar ); log1.finish_block(); log1.start_block( 5u ); + log1.start_transaction( transaction_id_type() ); + log1.start_action( 4ull, alice, alice, foo ); + log1.abort_block(); + log1.start_block( 5u ); log1.close(); } From 18f69d2bd567d40cc58bb0c9280f3497c24d0603 Mon Sep 17 00:00:00 2001 From: arhag Date: Sat, 18 May 2019 18:51:24 -0400 Subject: [PATCH 22/88] improve intrinsic logging support for transactions (particularly deferred transactions) --- libraries/chain/controller.cpp | 38 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 55a7ceab1a7..6422605e945 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1142,8 +1142,16 @@ struct controller_impl { uint32_t cpu_time_to_bill_us = billed_cpu_time_us; - // TODO: Figure out how to handle deferred transactions. - // It seems like I need to augment intrinsic_debug_log to support transaction level rollback. + auto abort_transaction_in_intrinsic_log_on_exit = fc::make_scoped_exit( + [abort_transaction=static_cast(intrinsic_log), this] { + if( abort_transaction ) { + intrinsic_log->abort_transaction(); + } + }); + + if( intrinsic_log ) { + intrinsic_log->start_transaction( gtrx.trx_id ); + } transaction_context trx_context( self, dtrx, gtrx.trx_id ); trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource @@ -1186,6 +1194,7 @@ struct controller_impl { undo_session.squash(); restore.cancel(); + abort_transaction_in_intrinsic_log_on_exit.cancel(); return trace; } catch( const disallowed_transaction_extensions_bad_block_exception& ) { @@ -1216,6 +1225,7 @@ struct controller_impl { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); + abort_transaction_in_intrinsic_log_on_exit.cancel(); return trace; } trace->elapsed = fc::time_point::now() - trx_context.start; @@ -1234,10 +1244,6 @@ struct controller_impl { if ( !subjective ) { // hard failure logic - if( intrinsic_log ) { - intrinsic_log->start_transaction( gtrx.trx_id ); - } - if( !explicit_billed_cpu_time ) { auto& rl = self.get_mutable_resource_limits_manager(); rl.update_account_usage( trx_context.bill_to_accounts, block_timestamp_type(self.pending_block_time()).slot ); @@ -1259,6 +1265,7 @@ struct controller_impl { emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); + abort_transaction_in_intrinsic_log_on_exit.cancel(); } else { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); @@ -1318,9 +1325,18 @@ struct controller_impl { const signed_transaction& trn = trx->packed_trx()->get_signed_transaction(); transaction_context trx_context(self, trn, trx->id(), start); + + auto abort_transaction_in_intrinsic_log_on_exit = fc::make_scoped_exit( + [abort_transaction=static_cast(intrinsic_log), this] { + if( abort_transaction ) { + intrinsic_log->abort_transaction(); + } + }); + if( intrinsic_log ) { intrinsic_log->start_transaction( trx_context.id ); } + if ((bool)subjective_cpu_leeway && pending->_block_status == controller::block_status::incomplete) { trx_context.leeway = *subjective_cpu_leeway; } @@ -1387,6 +1403,7 @@ struct controller_impl { trx_context.undo(); } else { restore.cancel(); + abort_transaction_in_intrinsic_log_on_exit.cancel(); trx_context.squash(); } @@ -2006,6 +2023,15 @@ struct controller_impl { } pending.reset(); protocol_features.popped_blocks_to( head->block_num ); + + // intrinsic_log is only supported in replay at the moment and abort_block should not be called during replay. + // But this is here since intrinsic_log already supports abort_block and it sets things up for the future + // in case we want to adapt the intrinsic_log to working during regular operation. + // In that case, we would also need to add support for popping committed blocks from the intrinsic log + // and call the appropriate function from controller_impl::pop_block(). + if( intrinsic_log ) { + intrinsic_log->abort_block(); + } } } From 8443d9ae25d7efa1bfb2e16892257b795281d9f2 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 20 May 2019 11:59:45 -0400 Subject: [PATCH 23/88] add intrinsic logging with hash of WASM linear memory and hash of arguments (except for pointers which are currently not supported) --- .../eosio/chain/webassembly/common.hpp | 27 ++++++++++++++++--- .../include/eosio/chain/webassembly/wabt.hpp | 20 ++++++++++---- .../include/eosio/chain/webassembly/wavm.hpp | 16 ++++++++--- libraries/chain/webassembly/wabt.cpp | 6 +++++ libraries/chain/webassembly/wavm.cpp | 9 +++++++ 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/common.hpp b/libraries/chain/include/eosio/chain/webassembly/common.hpp index 723bd3039b6..2ef02e9d9a5 100644 --- a/libraries/chain/include/eosio/chain/webassembly/common.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/common.hpp @@ -7,7 +7,7 @@ using namespace fc; -namespace eosio { namespace chain { +namespace eosio { namespace chain { class apply_context; class transaction_context; @@ -23,7 +23,7 @@ namespace eosio { namespace chain { return T(ctx); } }; - + template<> struct class_from_wasm { /** @@ -73,8 +73,15 @@ namespace eosio { namespace chain { return static_cast(value); } + template + friend inline DataStream& operator<<( DataStream& ds, const array_ptr& ) { + uint64_t zero = 0; + ds.write( (const char*)&zero, sizeof(zero) ); // Serializing pointers is not currently supported + return ds; + } + T *value; - }; + }; /** * class to represent an in-wasm-memory char array that must be null terminated @@ -95,7 +102,21 @@ namespace eosio { namespace chain { return static_cast(value); } + template + friend inline DataStream& operator<<( DataStream& ds, const null_terminated_ptr& ) { + uint64_t zero = 0; + ds.write( (const char*)&zero, sizeof(zero) ); // Serializing pointers is not currently supported + return ds; + } + char *value; }; + template + digest_type calc_arguments_hash( Params&&... params ) { + digest_type::encoder enc; + (fc::raw::pack( enc, std::forward(params) ), ...); + return enc.result(); + } + } } // eosio::chain diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 31456dc1dda..dbc827f5a33 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -35,7 +35,7 @@ struct intrinsic_registrator { struct intrinsic_func_info { FuncSignature sig; intrinsic_fn func; - }; + }; static auto& get_map(){ static map> _map; @@ -395,13 +395,13 @@ struct intrinsic_invoker_impl, size_t, Inputs...>> std::vector > copy(length > 0 ? length : 1); T* copy_ptr = ©[0]; memcpy( (void*)copy_ptr, (void*)base, length * sizeof(T) ); - Ret ret = Then(vars, static_cast>(copy_ptr), length, rest..., args, (uint32_t)offset - 2); + Ret ret = Then(vars, static_cast>(copy_ptr), length, rest..., args, (uint32_t)offset - 2); memcpy( (void*)base, (void*)copy_ptr, length * sizeof(T) ); return ret; } return Then(vars, static_cast>(base), length, rest..., args, (uint32_t)offset - 2); }; - + template static const auto fn() { return next_step::template fn>(); @@ -525,7 +525,7 @@ struct intrinsic_invoker_impl> { memcpy( (void*)©, (void*)base, sizeof(T) ); Ret ret = Then(vars, ©, rest..., args, (uint32_t)offset - 1); memcpy( (void*)base, (void*)©, sizeof(T) ); - return ret; + return ret; } return Then(vars, base, rest..., args, (uint32_t)offset - 1); }; @@ -632,7 +632,7 @@ struct intrinsic_invoker_impl> { memcpy( (void*)©, (void*)base, sizeof(T) ); Ret ret = Then(vars, copy, rest..., args, (uint32_t)offset - 1); memcpy( (void*)base, (void*)©, sizeof(T) ); - return ret; + return ret; } return Then(vars, *base, rest..., args, (uint32_t)offset - 1); } @@ -646,6 +646,8 @@ struct intrinsic_invoker_impl> { extern apply_context* fixme_context; +digest_type calc_memory_hash( const Memory& mem ); + /** * forward declaration of a wrapper class to call methods of the class */ @@ -656,6 +658,10 @@ struct intrinsic_function_invoker { template static Ret wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues&, int) { class_from_wasm::value(vars.ctx).checktime(); + auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); + if( intrinsic_log ) { + intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); + } return (class_from_wasm::value(vars.ctx).*Method)(params...); } @@ -672,6 +678,10 @@ struct intrinsic_function_invoker { template static void_type wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues& args, int offset) { class_from_wasm::value(vars.ctx).checktime(); + auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); + if( intrinsic_log ) { + intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); + } (class_from_wasm::value(vars.ctx).*Method)(params...); return void_type(); } diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 5bce9db8b40..4885e8c1e00 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -43,13 +43,13 @@ template inline array_ptr array_ptr_impl (running_instance_context& ctx, U32 ptr, size_t length) { MemoryInstance* mem = ctx.memory; - if (!mem) + if (!mem) Runtime::causeException(Exception::Cause::accessViolation); size_t mem_total = IR::numBytesPerPage * Runtime::getMemoryNumPages(mem); if (ptr >= mem_total || length > (mem_total - ptr) / sizeof(T)) Runtime::causeException(Exception::Cause::accessViolation); - + T* ret_ptr = (T*)(getMemoryBaseAddress(mem) + ptr); return array_ptr((T*)(getMemoryBaseAddress(mem) + ptr)); @@ -408,7 +408,7 @@ struct intrinsic_invoker_impl, size_t, Inputs...>, std::vector > copy(length > 0 ? length : 1); T* copy_ptr = ©[0]; memcpy( (void*)copy_ptr, (void*)base, length * sizeof(T) ); - Ret ret = Then(ctx, static_cast>(copy_ptr), length, rest..., translated...); + Ret ret = Then(ctx, static_cast>(copy_ptr), length, rest..., translated...); memcpy( (void*)base, (void*)copy_ptr, length * sizeof(T) ); return ret; } @@ -633,6 +633,8 @@ struct intrinsic_invoker_impl, std::tuple static Ret wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); + auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); + if( intrinsic_log ) { + intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); + } return (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); } @@ -662,6 +668,10 @@ struct intrinsic_function_invoker { template static void_type wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); + auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); + if( intrinsic_log ) { + intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); + } (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); return void_type(); } diff --git a/libraries/chain/webassembly/wabt.cpp b/libraries/chain/webassembly/wabt.cpp index a23919e0ec6..49beb8ea295 100644 --- a/libraries/chain/webassembly/wabt.cpp +++ b/libraries/chain/webassembly/wabt.cpp @@ -100,4 +100,10 @@ void wabt_runtime::immediately_exit_currently_running_module() { throw wasm_exit(); } +digest_type calc_memory_hash( const Memory& mem ) { + digest_type::encoder enc; + enc.write( mem.data.data(), mem.data.size() ); + return enc.result(); +} + }}}} diff --git a/libraries/chain/webassembly/wavm.cpp b/libraries/chain/webassembly/wavm.cpp index e006d237c5c..e1bcc9e490e 100644 --- a/libraries/chain/webassembly/wavm.cpp +++ b/libraries/chain/webassembly/wavm.cpp @@ -160,4 +160,13 @@ void wavm_runtime::immediately_exit_currently_running_module() { #endif } +digest_type calc_memory_hash( MemoryInstance* mem ) { + const char* base_of_memory = (const char*)( getMemoryBaseAddress(mem) ); + std::size_t mem_size = IR::numBytesPerPage * Runtime::getMemoryNumPages(mem); + + digest_type::encoder enc; + enc.write( base_of_memory, mem_size ); + return enc.result(); +} + }}}} From 393356ffacf264b754de8d70d8e07d2078775dae Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 20 May 2019 17:13:26 -0400 Subject: [PATCH 24/88] stuff --- libraries/CMakeLists.txt | 2 +- libraries/chain/webassembly/eos-vm.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 9727f511f19..e95544bcd7e 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -14,7 +14,7 @@ set(RUN_RE2C OFF CACHE BOOL "Run re2c") set(WITH_EXCEPTIONS ON CACHE BOOL "Build with exceptions enabled" FORCE) add_subdirectory( wabt ) -set(USE_EXISTING_SOFTFLOAT ON) +set(USE_EXISTING_SOFTFLOAT ON CACHE BOOL "use pre-exisiting softfloat lib") set(ENABLE_TESTS OFF) set(ENABLE_ADDRESS_SANITIZER OFF) set(ENABLE_UNDEFINED_BEHAVIOR_SANITIZER OFF) diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index c47c10b3c81..6c7420ed69f 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -38,14 +38,11 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { eos_vm_runtime::eos_vm_runtime() {} std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { - std::vector cb((uint8_t*)code_bytes, (uint8_t*)code_bytes+code_size); - //std::vector cb; - //cb.resize(code_size); - //memcpy(cb.data(), code_bytes, code_size); std::ofstream mf("temp.wasm"); - mf.write((char*)cb.data(), cb.size()); + mf.write((char*)code_bytes, code_size); mf.close(); - std::unique_ptr bkend = std::make_unique( cb ); + wasm_code_ptr code((uint8_t*)code_bytes, 0); + std::unique_ptr bkend = std::make_unique(code, code_size); registered_host_functions::resolve(bkend->get_module()); _bkend = bkend.get(); return std::make_unique(std::move(bkend)); From 04be48745a948e4fbdb9db085a8b3995913812b9 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 20 May 2019 17:53:31 -0400 Subject: [PATCH 25/88] added eos-vm as submodule --- .gitmodules | 3 +++ libraries/eos-vm | 1 + 2 files changed, 4 insertions(+) create mode 160000 libraries/eos-vm diff --git a/.gitmodules b/.gitmodules index 80fe86f626d..4e25bc2f43c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ [submodule "libraries/yubihsm"] path = libraries/yubihsm url = https://github.com/Yubico/yubihsm-shell +[submodule "libraries/eos-vm"] + path = libraries/eos-vm + url = git@github.com:eosio/eos-vm diff --git a/libraries/eos-vm b/libraries/eos-vm new file mode 160000 index 00000000000..4f1c727ba54 --- /dev/null +++ b/libraries/eos-vm @@ -0,0 +1 @@ +Subproject commit 4f1c727ba5484aee95bc166c2cfaf0c2aa9c81d4 From bf4635b94814e87e955b439320be8778d2324ff4 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 22 May 2019 00:22:15 -0400 Subject: [PATCH 26/88] create initial scaffolding for a new utility program: intrinsic-debug-log The program uses a new wrapper over boost::program_options that makes it easy to do cleos like subcommands. --- programs/CMakeLists.txt | 1 + programs/intrinsic-log-util/CMakeLists.txt | 26 + programs/intrinsic-log-util/main.cpp | 224 +++++++ programs/intrinsic-log-util/subcommand.hpp | 651 +++++++++++++++++++++ 4 files changed, 902 insertions(+) create mode 100644 programs/intrinsic-log-util/CMakeLists.txt create mode 100644 programs/intrinsic-log-util/main.cpp create mode 100644 programs/intrinsic-log-util/subcommand.hpp diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index a40ef3b423c..6cb1e5bd635 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory( cleos ) add_subdirectory( keosd ) add_subdirectory( eosio-launcher ) add_subdirectory( eosio-blocklog ) +add_subdirectory( intrinsic-log-util ) diff --git a/programs/intrinsic-log-util/CMakeLists.txt b/programs/intrinsic-log-util/CMakeLists.txt new file mode 100644 index 00000000000..6850d4396df --- /dev/null +++ b/programs/intrinsic-log-util/CMakeLists.txt @@ -0,0 +1,26 @@ +add_executable( intrinsic-log-util main.cpp ) + +if( UNIX AND NOT APPLE ) + set(rt_library rt ) +endif() + +find_package( Gperftools QUIET ) +if( GPERFTOOLS_FOUND ) + message( STATUS "Found gperftools; compiling intrinsic-log-util with TCMalloc") + list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) +endif() + +target_include_directories(intrinsic-log-util PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +target_link_libraries( intrinsic-log-util + PRIVATE appbase + PRIVATE eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + +copy_bin( intrinsic-log-util ) +#install( TARGETS +# intrinsic-log-util +# +# RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} +# LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} +# ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} +#) diff --git a/programs/intrinsic-log-util/main.cpp b/programs/intrinsic-log-util/main.cpp new file mode 100644 index 00000000000..7089157c70f --- /dev/null +++ b/programs/intrinsic-log-util/main.cpp @@ -0,0 +1,224 @@ +/** + * @file + * @copyright defined in eosio/LICENSE.txt + */ +#include "subcommand.hpp" +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace eosio; +using namespace eosio::chain; + +namespace bfs = boost::filesystem; +namespace bpo = boost::program_options; + +struct intrinsic_log { + intrinsic_log() + {} + + fc::optional log; +}; + +struct print_block_subcommand + : public subcommand< + print_block_subcommand, + subcommand_style::terminal_subcommand_with_positional_arguments + > +{ + static constexpr const char* name = "block"; + + static std::string get_description() { + return "Print a specific block"; + } + + void set_options( bpo::options_description& options )const { + options.add_options() + ("output-file,o", bpo::value(), + "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") + ; + } + + bpo::positional_options_description + get_positional_options( bpo::options_description& options, positional_descriptions& pos_desc )const { + options.add_options() + ("block-num", bpo::value(), + "the block number to print") + ; + + std::array pos_args = { + "block-num" + }; + + return build_positional_descriptions( pos_args, options, pos_desc ); + } +}; + +struct print_blocks_subcommand + : public subcommand< + print_blocks_subcommand, + subcommand_style::terminal_subcommand_without_positional_arguments + > +{ + static constexpr const char* name = "blocks"; + + static std::string get_description() { + return "Print a range of blocks"; + } + + void set_options( bpo::options_description& options )const { + options.add_options() + ("output-file,o", bpo::value(), + "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") + ("first,f", bpo::value()->default_value(0), + "the first block number to print") + ("last,l", bpo::value()->default_value(std::numeric_limits::max()), + "the first block number to print") + ; + } + + void initialize( bpo::variables_map&& vm ) { + if( vm.count( "first" ) ) { + first_block_num = vm.at( "first" ).as(); + } + + if( vm.count( "last" ) ) { + last_block_num = vm.at( "last" ).as(); + FC_ASSERT( last_block_num >= first_block_num, "invalid range" ); + } + } + + uint32_t first_block_num = 0; + uint32_t last_block_num = 0; +}; + +struct print_subcommand + : public subcommand< + print_subcommand, + subcommand_style::contains_subcommands, + print_block_subcommand, + print_blocks_subcommand + > +{ + static constexpr const char* name = "print"; + + static std::string get_description() { + return "Print blocks in a log file"; + } + + void set_options( bpo::options_description& options )const { + options.add_options() + ("no-pretty-print", bpo::bool_switch()->default_value(false), "avoid pretty printing") + ; + } +}; + +struct diff_subcommand + : public subcommand< + diff_subcommand, + subcommand_style::terminal_subcommand_with_positional_arguments + > +{ + static constexpr const char* name = "diff"; + + static std::string get_description() { + return "Find the differences between two log files"; + } + + void set_options( bpo::options_description& options )const { + options.add_options() + ("start-block", bpo::value()->default_value(0), + "ignore any differences prior to this block") + ; + } + + bpo::positional_options_description + get_positional_options( bpo::options_description& options, positional_descriptions& pos_desc )const { + options.add_options() + ("first-log", bpo::value(), + "path to the first log file to compare") + ("second-log", bpo::value(), + "path to the second log file to compare") + ; + + std::array pos_args = { + "first-log", + "second-log" + }; + + return build_positional_descriptions( pos_args, options, pos_desc ); + } +}; + +struct root_command + : public subcommand< + root_command, + subcommand_style::contains_subcommands, + print_subcommand, + diff_subcommand + > +{ + static constexpr const char* name = "Generic"; + + static std::string get_description() { + return "Utility to inspect intrinsic log files"; + } + + void set_options( bpo::options_description& options ) { + options.add_options() + ("help,h", bpo::bool_switch(&print_help)->default_value(false), "Print this help message and exit.") + ("no-detail", bpo::bool_switch()->default_value(false), "Temporarily added for testing purposes.") + ; + } + + bool print_help = false; +}; + +int main( int argc, const char** argv ) { + try { + root_command rc; + auto res = parse_program_options_extended( rc, argc, argv, std::cout, &std::cerr ); + + if( std::holds_alternative( res ) ) { + FC_ASSERT( false, "parse error occurred" ); + } else if( std::holds_alternative( res ) ) { + return -1; + } else if( std::holds_alternative( res ) ) { + return 0; + } + + const auto& pm = std::get( res ); + + if( argc == 1 || rc.print_help ) { + print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); + return 0; + } + + std::ios::sync_with_stdio(false); // for potential performance boost for large log files + + + + } catch( const fc::exception& e ) { + elog( "${e}", ("e", e.to_detail_string())); + return -1; + } catch( const boost::exception& e ) { + elog("${e}", ("e",boost::diagnostic_information(e))); + return -1; + } catch( const std::exception& e ) { + elog("${e}", ("e",e.what())); + return -1; + } catch( ... ) { + elog("unknown exception"); + return -1; + } + + return 0; +} diff --git a/programs/intrinsic-log-util/subcommand.hpp b/programs/intrinsic-log-util/subcommand.hpp new file mode 100644 index 00000000000..8b2293f47fe --- /dev/null +++ b/programs/intrinsic-log-util/subcommand.hpp @@ -0,0 +1,651 @@ +/** + * @file + * @copyright defined in eosio/LICENSE.txt + */ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +//#include +#include +#include +#include + +namespace eosio { + +namespace bfs = boost::filesystem; +namespace bpo = boost::program_options; + +enum class subcommand_style { + contains_subcommands, + terminal_subcommand_with_positional_arguments, + terminal_subcommand_without_positional_arguments +}; + +namespace detail { + template struct overloaded : Ts... { using Ts::operator()...; }; + template overloaded(Ts...) -> overloaded; + + template + struct has_name : std::false_type {}; + + template + struct has_name : std::true_type {}; + + template + struct has_description : std::false_type {}; + + template + struct has_description : std::true_type {}; + + template + struct has_initialize : std::false_type {}; + + template + struct has_initialize().initialize( std::move( std::declval() ) ), 0)> + : std::true_type + {}; + + template + bool visit_by_name_helper( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { + using variant_type = std::variant; + + if constexpr( I < std::variant_size_v ) + { + if constexpr( has_name< std::variant_alternative_t >::value ) { + if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { + std::forward(visitor)( std::get( var ) ); + return true; + } + } + return visit_by_name_helper( var, + subcommand_name, + std::forward(visitor) ); + } else { + return false; + } + } + + template + bool visit_by_name( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { + return visit_by_name_helper<0>( var, subcommand_name, std::forward( visitor ) ); + } + + template + bool construct_variant_by_name_helper( Variant& var, + const std::string& subcommand_name, + Visitor&& visitor, + Args&&... args ) + { + if constexpr( I < std::variant_size_v ) { + if constexpr( has_name< std::variant_alternative_t >::value ) { + if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { + std::forward(visitor)( var.template emplace( std::forward(args)... ) ); + return true; + } + } + return construct_variant_by_name_helper( var, + subcommand_name, + std::forward(visitor), + std::forward(args)... ); + } else { + return false; + } + } + + template + bool construct_variant_by_name( Variant& var, + const std::string& subcommand_name, + Args&&... args ) + { + return construct_variant_by_name_helper<0>( var, + subcommand_name, + []( auto&& ) {}, // do nothing in visitor + std::forward(args)... ); + } + + template + bool construct_and_visit_variant_by_name( Variant& var, + const std::string& subcommand_name, + Visitor&& visitor, + Args&&... args ) + { + return construct_variant_by_name_helper<0>( var, + subcommand_name, + std::forward(visitor), + std::forward(args)... ); + } + + template + void static_visit_all_helper( Visitor&& visitor ) + { + if constexpr( I < std::variant_size_v ) { + if constexpr( has_name< std::variant_alternative_t >::value ) { + std::string desc; + if constexpr( has_description< std::variant_alternative_t >::value ) { + desc = std::variant_alternative_t::get_description(); + } + visitor( std::variant_alternative_t::name, desc ); + } + return static_visit_all_helper( std::forward(visitor) ); + } + } + + template + void static_visit_all( Visitor&& visitor ) { + static_visit_all_helper<0, Variant>( std::forward(visitor) ); + } + + struct no_subcommand {}; + + class subcommand_untyped_base {}; + + template + class subcommand_base : public subcommand_untyped_base { + static_assert( (std::is_default_constructible_v && ...) ); + static_assert( (std::is_base_of_v && ...), "subcommands must derive from subcommand type" ); + + protected: + subcommand_base() = default; + friend T; + + bpo::options_description _get_options( const std::vector& subcommand_context ) + { + std::string options_name; + if( subcommand_context.size() > 0 ) { + for( const auto& s : subcommand_context ) { + options_name += s; + options_name += " "; + } + } else { + options_name += T::name; + options_name += " "; + } + options_name += "options"; + + bpo::options_description options( options_name ); + T& derived = static_cast( *this ); + derived.set_options( options ); + return options; + } + + template + static std::string _get_description() { + if constexpr( detail::has_description::value ) { + return U::get_description(); + } else { + return {}; + } + } + + template + void _initialize( bpo::variables_map&& vm ) { + if constexpr( detail::has_initialize::value ) { + T& derived = static_cast( *this ); + derived.initialize( std::move(vm) ); + } + } + }; + +} /// namespace eosio::detail + +struct subcommand_description { + subcommand_description( const std::string& subcommand_name, const std::string& description ) + :subcommand_name( subcommand_name ) + ,description( description ) + {} + + std::string subcommand_name; + std::string description; +}; + +using subcommand_descriptions = std::vector; + +struct positional_description { + positional_description( const std::string& positional_name, const std::string& description ) + :positional_name( positional_name ) + ,description( description ) + {} + + std::string positional_name; + std::string description; +}; + +using positional_descriptions = std::vector; + +struct parse_metadata { + parse_metadata( const std::vector& context ) + :subcommand_context( context ) + {} + + /** @pre subcommand_descriptions alternative of other_description must be present */ + void add_subcommand_description( const std::string& subcommand, const std::string& description ) + { + std::get( other_description ).emplace_back( subcommand, description ); + } + + void set_positional_descriptions( const positional_descriptions& pos_desc ) { + other_description.emplace( pos_desc ); + } + + void set_positional_descriptions( positional_descriptions&& pos_desc ) { + other_description.emplace( std::move(pos_desc) ); + } + + std::vector subcommand_context; + std::string subcommand_desc; + std::shared_ptr opts_description; + std::variant other_description; + bool initialization_skipped = false; +}; + +template +class subcommand; + +template +class subcommand + : public detail::subcommand_base +{ +protected: + using base_type = detail::subcommand_base; + using subcommands_type = std::variant; + + subcommands_type _subcommand; + +public: + + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + bpo::options_description subcommand_options{base_type::_get_options(subcommand_context)}; + + auto subcommand_options_ptr = std::make_shared( subcommand_options ); + + subcommand_options.add_options() + ("__command", bpo::value(), "command to execute" ) + ("__subargs", bpo::value>(), "arguments for command" ) + ; + + bpo::positional_options_description pos; + pos.add( "__command", 1 ).add( "__subargs", -1 ); + + bpo::parsed_options parsed = bpo::command_line_parser( opts ) + .options( subcommand_options ) + .positional( pos ) + .allow_unregistered() + .run(); + + bpo::variables_map vm; + + bpo::variables_map vm_temp; + bpo::store( parsed, vm_temp ); + + if( vm_temp.count( "__command" ) > 0 ) { + std::string cmd_name = vm_temp["__command"].as(); + + std::vector inner_opts = bpo::collect_unrecognized( parsed.options, bpo::include_positional ); + FC_ASSERT( inner_opts.size() > 0, "unexpected error" ); + FC_ASSERT( *inner_opts.begin() == cmd_name, + "unrecognized option '${option}'", + ("option", *inner_opts.begin()) + ); + inner_opts.erase( inner_opts.begin() ); + + bool success = detail::construct_and_visit_variant_by_name( _subcommand, cmd_name, + [&inner_opts, &pm, &cmd_name, &subcommand_options_ptr, &skip_initialize, context = subcommand_context] + ( auto&& s ) mutable { + context.emplace_back( cmd_name ); + pm = s.parse( inner_opts, skip_initialize, context ); + FC_ASSERT( pm.opts_description, "unexpected error" ); + subcommand_options_ptr->add( *pm.opts_description ); + pm.opts_description = subcommand_options_ptr; + } + ); + FC_ASSERT( success, "unrecognized sub-command '${name}' within context: ${context}", + ("name", cmd_name)("context", subcommand_context) + ); + vm = std::move(vm_temp); + } else { + bpo::parsed_options parsed2 = bpo::command_line_parser( opts ) + .options( *subcommand_options_ptr ) + .run(); + bpo::store( parsed2, vm ); + + detail::static_visit_all>( + [&pm]( const char* name, const std::string& desc ) { + pm.add_subcommand_description( name, desc ); + } + ); + + pm.opts_description = std::move(subcommand_options_ptr); + pm.subcommand_desc = base_type::_get_description(); + pm.initialization_skipped = skip_initialize; + } + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + + return pm; + } + + const subcommands_type& get_subcommand()const { return _subcommand; } +}; + +template +class subcommand + : public detail::subcommand_base +{ +protected: + using base_type = detail::subcommand_base; + +public: + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + pm.subcommand_desc = base_type::_get_description(); + pm.opts_description = std::make_shared( base_type::_get_options(subcommand_context) ); + + bpo::options_description subcommand_options{ *pm.opts_description }; + positional_descriptions pos_desc; + const auto& pos = _get_positional_options( subcommand_options, pos_desc ); + pm.set_positional_descriptions( pos_desc ); + + bpo::variables_map vm; + + bpo::store( bpo::command_line_parser( opts ) + .options( subcommand_options ) + .positional( pos ) + .run(), + vm ); + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + pm.initialization_skipped = skip_initialize; + + return pm; + } + +protected: + bpo::positional_options_description _get_positional_options( bpo::options_description& options, + positional_descriptions& pos_desc )const + { + const T& derived = static_cast( *this ); + return derived.get_positional_options( options, pos_desc ); + } +}; + +template +class subcommand + : public detail::subcommand_base +{ +protected: + using base_type = detail::subcommand_base; + +public: + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + pm.subcommand_desc = base_type::_get_description(); + pm.opts_description = std::make_shared( base_type::_get_options(subcommand_context) ); + + bpo::variables_map vm; + + bpo::store( bpo::command_line_parser( opts ) + .options( *pm.opts_description ) + .run(), + vm ); + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + pm.initialization_skipped = skip_initialize; + + return pm; + } +}; + +template +bpo::positional_options_description +build_positional_descriptions( const std::array& pos_names, + const bpo::options_description& options, + positional_descriptions& pos_desc ) +{ + bpo::positional_options_description pos; + + for( std::size_t i = 0; i < pos_names.size(); ++i ) { + pos.add( pos_names[i].c_str(), 1 ); + const bpo::option_description* opt = options.find_nothrow( pos_names[i], false ); + if( opt ) { + pos_desc.emplace_back( pos_names[i], opt->description() ); + } else { + pos_desc.emplace_back( pos_names[i], "" ); + } + } + + return pos; +} + +void print_subcommand_help( std::ostream& stream, const std::string& program_name, const parse_metadata& pm ) { + const subcommand_descriptions* subcommands = std::get_if( &pm.other_description ); + bool subcommands_present = (subcommands && subcommands->size() > 0); + + const positional_descriptions* positionals = std::get_if( &pm.other_description ); + bool positionals_present = (positionals && positionals->size() > 0); + + auto formatting_width = pm.opts_description->get_option_column_width() - 2; + + std::string subcommand_usage; + for( const auto& s : pm.subcommand_context ) { + subcommand_usage += " "; + subcommand_usage += s; + } + if( subcommands_present ) { + subcommand_usage += " SUBCOMMAND"; + } + + if( pm.subcommand_desc.size() > 0 ) { + stream << pm.subcommand_desc << std::endl; + } + + stream << "Usage: " << program_name + << subcommand_usage + << " [OPTIONS]"; + + if( positionals_present ) { + for( const auto& p : *positionals ) { + stream << " " << p.positional_name; + } + } + + stream << std::endl << std::endl; + + if( positionals_present ) { + stream << "Positionals:" << std::endl; + + for( const auto& p : *positionals ) { + stream << " "; + auto orig_width = stream.width( formatting_width ); + stream << std::left << p.positional_name; + stream.width( orig_width ); + stream << p.description << std::endl; + } + + stream << std::endl; + } + + pm.opts_description->print( stream ); + + if( subcommands_present ) { + stream << std::endl; + + std::string subcommands_title; + if( pm.subcommand_context.size() > 0 ) { + for( const auto& s : pm.subcommand_context ) { + subcommands_title += s; + subcommands_title += " "; + } + subcommands_title += "subcommands:"; + } else { + subcommands_title = "Subcommands:"; + } + + stream << subcommands_title << std::endl; + for( const auto& s : *subcommands ) { + stream << " "; + auto orig_width = stream.width( formatting_width ); + stream << std::left << s.subcommand_name; + stream.width( orig_width ); + stream << s.description << std::endl; + } + } +} + +bool print_autocomplete_suggestions( std::ostream& stream, const parse_metadata& pm, const std::string& last_token ) { + bool printed_something = false; + + if( std::holds_alternative( pm.other_description ) ) { + for( const auto& s : std::get( pm.other_description ) ) { + if( s.subcommand_name.find( last_token ) != 0 ) continue; + if( printed_something ) + stream << " "; + stream << s.subcommand_name; + printed_something = true; + } + } + + if( last_token.size() > 0 ) { + for( const auto& opt : pm.opts_description->options() ) { + //if( opt->match( last_token, true, false, false ) == bpo::option_description::no_match ) continue; + // The option_description::match function doesn't seem to work. + + const auto& short_name = opt->canonical_display_name( bpo::command_line_style::allow_dash_for_short ); + if( short_name.size() > 0 && short_name[0] == '-' && short_name.find( last_token ) == 0 ) { + if( printed_something ) + stream << " "; + stream << short_name; + printed_something = true; + } + + const auto& long_name = opt->canonical_display_name( bpo::command_line_style::allow_long ); + if( long_name.size() > 0 && long_name[0] == '-' && long_name.find( last_token ) == 0 ) { + if( printed_something ) + stream << " "; + stream << long_name; + printed_something = true; + } + } + } + + return printed_something; +} + +template +std::optional +parse_program_options( T& root, const std::vector& opts, //int argc, const char** argv, + bool skip_initialize = false, bool avoid_throwing = false ) +{ + try { + return root.parse( opts, skip_initialize ); + } catch( ... ) { + if( !avoid_throwing ) throw; + } + + return {}; +} + +struct autocomplete_failure {}; +struct autocomplete_success {}; +struct parse_failure {}; + +template +std::variant +parse_program_options_extended( T& root, int argc, const char** argv, + std::ostream& out_stream, std::ostream* err_stream = nullptr, + bool avoid_throwing = false ) +{ + if( argc <= 0 ) { + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "command line arguments should at least include program name" ); + } + + std::string program_name{ bfs::path( argv[0] ).filename().generic_string() }; + + bool autocomplete_mode = false; + + std::vector opts; + opts.reserve( argc - 1 ); + for( int i = 1; i < argc; ++i ) { + opts.emplace_back( std::string( *(argv + i) ) ); + if( opts.back().compare( "-" ) == 0 ) { + if( autocomplete_mode ) return autocomplete_failure{}; + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "- is not allowed by itself in the command line arguments" ); + } + if( opts.back().compare( "--" ) == 0 ) { + if( i != 1 ) { + if( autocomplete_mode ) return autocomplete_failure{}; + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "-- is not allowed by itself in the command line arguments" ); + } + opts.pop_back(); + autocomplete_mode = true; + } + } + + if( !autocomplete_mode ) { + const auto pm = parse_program_options( root, opts, false, avoid_throwing ); + if( !pm ) return parse_failure{}; + return *pm; + } + + // Autocomplete helper logic: + + std::string last_token; + if( opts.size() > 0 ) { + if( opts.back().size() == 0 ) { + opts.pop_back(); + } else { + last_token = opts.back(); + } + } + + if( last_token.size() > 0 ) { + if( opts.size() == 0 ) return autocomplete_failure{}; + opts.pop_back(); + } + + auto pm = parse_program_options( root, opts, true, true ); + if( !pm ) return autocomplete_failure{}; + + if( last_token.size() > 0 ) { + if( print_autocomplete_suggestions( out_stream, *pm, last_token ) ) { + out_stream << std::endl; + } else { + return autocomplete_failure{}; + } + } + + if( err_stream ) { + *err_stream << std::endl; + print_subcommand_help( *err_stream, program_name, *pm ); + } + + return autocomplete_success{}; +} + +} /// namespace eosio From 268439e73c19db0fa8cdbb2dd2069d362321a968 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 22 May 2019 21:25:55 -0400 Subject: [PATCH 27/88] cleanup subcommand code into eosio::cli_parser namespace; rename header file to cli_parser.hpp; add dispatch support to cli_parser --- programs/intrinsic-log-util/cli_parser.hpp | 790 +++++++++++++++++++++ programs/intrinsic-log-util/main.cpp | 60 +- programs/intrinsic-log-util/subcommand.hpp | 651 ----------------- 3 files changed, 838 insertions(+), 663 deletions(-) create mode 100644 programs/intrinsic-log-util/cli_parser.hpp delete mode 100644 programs/intrinsic-log-util/subcommand.hpp diff --git a/programs/intrinsic-log-util/cli_parser.hpp b/programs/intrinsic-log-util/cli_parser.hpp new file mode 100644 index 00000000000..e97a279909b --- /dev/null +++ b/programs/intrinsic-log-util/cli_parser.hpp @@ -0,0 +1,790 @@ +/** + * @file + * @copyright defined in eosio/LICENSE.txt + */ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +//#include +#include + +namespace eosio::cli_parser { + + namespace bfs = boost::filesystem; + namespace bpo = boost::program_options; + + enum class subcommand_style { + contains_subcommands, + terminal_subcommand_with_positional_arguments, + terminal_subcommand_without_positional_arguments + }; + + struct subcommand_description { + subcommand_description( const std::string& subcommand_name, const std::string& description ) + :subcommand_name( subcommand_name ) + ,description( description ) + {} + + std::string subcommand_name; + std::string description; + }; + + using subcommand_descriptions = std::vector; + + struct positional_description { + positional_description( const std::string& positional_name, const std::string& description ) + :positional_name( positional_name ) + ,description( description ) + {} + + std::string positional_name; + std::string description; + }; + + using positional_descriptions = std::vector; + + struct autocomplete_failure {}; + struct autocomplete_success {}; + struct parse_failure {}; + + struct parse_metadata { + parse_metadata( const std::vector& context ) + :subcommand_context( context ) + {} + + /** @pre subcommand_descriptions alternative of other_description must be present */ + void add_subcommand_description( const std::string& subcommand, const std::string& description ) + { + std::get( other_description ).emplace_back( subcommand, description ); + } + + void set_positional_descriptions( const positional_descriptions& pos_desc ) { + other_description.emplace( pos_desc ); + } + + void set_positional_descriptions( positional_descriptions&& pos_desc ) { + other_description.emplace( std::move(pos_desc) ); + } + + std::vector subcommand_context; + std::string subcommand_desc; + std::shared_ptr opts_description; + std::variant other_description; + bool initialization_skipped = false; + }; + + namespace detail { + template struct overloaded : Ts... { using Ts::operator()...; }; + template overloaded(Ts...) -> overloaded; + + template + struct has_name : std::false_type {}; + + template + struct has_name : std::true_type {}; + + template + struct has_description : std::false_type {}; + + template + struct has_description : std::true_type {}; + + template + struct has_initialize : std::false_type {}; + + template + struct has_initialize().initialize( std::move( std::declval() ) ), 0)> + : std::true_type + {}; + + template + bool maybe_call_visitor( Visitor&& visitor, Args&&... args ) { + if constexpr( std::is_invocable_v ) { + std::forward(visitor)( std::forward(args)... ); + return true; + } else { + return false; + } + } + + template + bool reduce_tuple_helper( Ret& accumulator, Tuple&& t, + Extractor&& extract, Reducer&& reduce ) + { + if constexpr( I < std::tuple_size_v> ) { + if( !reduce( accumulator, I, extract( std::get( t ) ) ) ) + return false; + + return reduce_tuple_helper( accumulator, std::forward(t), + std::forward(extract), + std::forward(reduce) ); + } + + return true; + } + + template + bool reduce_tuple( Ret& accumulator, Tuple&& t, + Extractor&& extract, Reducer&& reduce ) + { + return reduce_tuple_helper<0>( accumulator, std::forward(t), + std::forward(extract), + std::forward(reduce) ); + } + + template + bool visit_by_name_helper( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { + using variant_type = std::variant; + + if constexpr( I < std::variant_size_v ) + { + if constexpr( has_name< std::variant_alternative_t >::value ) { + if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { + std::forward(visitor)( std::get( var ) ); + return true; + } + } + return visit_by_name_helper( var, + subcommand_name, + std::forward(visitor) ); + } else { + return false; + } + } + + template + bool visit_by_name( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { + return visit_by_name_helper<0>( var, subcommand_name, std::forward( visitor ) ); + } + + template + bool construct_variant_by_name_helper( Variant& var, + const std::string& subcommand_name, + Visitor&& visitor, + Args&&... args ) + { + if constexpr( I < std::variant_size_v ) { + if constexpr( has_name< std::variant_alternative_t >::value ) { + if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { + std::forward(visitor)( var.template emplace( std::forward(args)... ) ); + return true; + } + } + return construct_variant_by_name_helper( var, + subcommand_name, + std::forward(visitor), + std::forward(args)... ); + } else { + return false; + } + } + + template + bool construct_variant_by_name( Variant& var, + const std::string& subcommand_name, + Args&&... args ) + { + return construct_variant_by_name_helper<0>( var, + subcommand_name, + []( auto&& ) {}, // do nothing in visitor + std::forward(args)... ); + } + + template + bool construct_and_visit_variant_by_name( Variant& var, + const std::string& subcommand_name, + Visitor&& visitor, + Args&&... args ) + { + return construct_variant_by_name_helper<0>( var, + subcommand_name, + std::forward(visitor), + std::forward(args)... ); + } + + template + void static_visit_all_helper( Visitor&& visitor ) + { + if constexpr( I < std::variant_size_v ) { + if constexpr( has_name< std::variant_alternative_t >::value ) { + std::string desc; + if constexpr( has_description< std::variant_alternative_t >::value ) { + desc = std::variant_alternative_t::get_description(); + } + visitor( std::variant_alternative_t::name, desc ); + } + return static_visit_all_helper( std::forward(visitor) ); + } + } + + template + void static_visit_all( Visitor&& visitor ) { + static_visit_all_helper<0, Variant>( std::forward(visitor) ); + } + + struct no_subcommand {}; + + class subcommand_untyped_base {}; + + template + class subcommand_base : public subcommand_untyped_base { + static_assert( (std::is_default_constructible_v && ...) ); + static_assert( (std::is_base_of_v && ...), "subcommands must derive from subcommand type" ); + + protected: + subcommand_base() = default; + friend T; + + bpo::options_description _get_options( const std::vector& subcommand_context ) + { + std::string options_name; + if( subcommand_context.size() > 0 ) { + for( const auto& s : subcommand_context ) { + options_name += s; + options_name += " "; + } + } else { + options_name += T::name; + options_name += " "; + } + options_name += "options"; + + bpo::options_description options( options_name ); + T& derived = static_cast( *this ); + derived.set_options( options ); + return options; + } + + template + static std::string _get_description() { + if constexpr( has_description::value ) { + return U::get_description(); + } else { + return {}; + } + } + + template + void _initialize( bpo::variables_map&& vm ) { + if constexpr( has_initialize::value ) { + T& derived = static_cast( *this ); + derived.initialize( std::move(vm) ); + } + } + }; + + template + class subcommand; + + template + class subcommand + : public subcommand_base + { + protected: + using base_type = subcommand_base; + using subcommands_type = std::variant; + + static_assert( sizeof...(Us) > 0 ); + + subcommands_type _subcommand; + + public: + static constexpr bool has_subcommands = true; + + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + bpo::options_description subcommand_options{base_type::_get_options(subcommand_context)}; + + auto subcommand_options_ptr = std::make_shared( subcommand_options ); + + subcommand_options.add_options() + ("__command", bpo::value(), "command to execute" ) + ("__subargs", bpo::value>(), "arguments for command" ) + ; + + bpo::positional_options_description pos; + pos.add( "__command", 1 ).add( "__subargs", -1 ); + + bpo::parsed_options parsed = bpo::command_line_parser( opts ) + .options( subcommand_options ) + .positional( pos ) + .allow_unregistered() + .run(); + + bpo::variables_map vm; + + bpo::variables_map vm_temp; + bpo::store( parsed, vm_temp ); + + if( vm_temp.count( "__command" ) > 0 ) { + std::string cmd_name = vm_temp["__command"].as(); + + std::vector inner_opts = bpo::collect_unrecognized( parsed.options, bpo::include_positional ); + FC_ASSERT( inner_opts.size() > 0, "unexpected error" ); + FC_ASSERT( *inner_opts.begin() == cmd_name, + "unrecognized option '${option}'", + ("option", *inner_opts.begin()) + ); + inner_opts.erase( inner_opts.begin() ); + + bool success = construct_and_visit_variant_by_name( _subcommand, cmd_name, + [&inner_opts, &pm, &cmd_name, &subcommand_options_ptr, &skip_initialize, context = subcommand_context] + ( auto&& s ) mutable { + context.emplace_back( cmd_name ); + pm = s.parse( inner_opts, skip_initialize, context ); + FC_ASSERT( pm.opts_description, "unexpected error" ); + subcommand_options_ptr->add( *pm.opts_description ); + pm.opts_description = subcommand_options_ptr; + } + ); + FC_ASSERT( success, "unrecognized sub-command '${name}' within context: ${context}", + ("name", cmd_name)("context", subcommand_context) + ); + vm = std::move(vm_temp); + } else { + bpo::parsed_options parsed2 = bpo::command_line_parser( opts ) + .options( *subcommand_options_ptr ) + .run(); + bpo::store( parsed2, vm ); + + static_visit_all>( + [&pm]( const char* name, const std::string& desc ) { + pm.add_subcommand_description( name, desc ); + } + ); + + pm.opts_description = std::move(subcommand_options_ptr); + pm.subcommand_desc = base_type::_get_description(); + pm.initialization_skipped = skip_initialize; + } + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + + return pm; + } + + const subcommands_type& get_subcommand()const { return _subcommand; } + }; + + template + class subcommand + : public subcommand_base + { + protected: + using base_type = subcommand_base; + + public: + static constexpr bool has_subcommands = false; + + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + pm.subcommand_desc = base_type::_get_description(); + pm.opts_description = std::make_shared( + base_type::_get_options(subcommand_context) ); + + bpo::options_description subcommand_options{ *pm.opts_description }; + positional_descriptions pos_desc; + const auto& pos = _get_positional_options( subcommand_options, pos_desc ); + pm.set_positional_descriptions( pos_desc ); + + bpo::variables_map vm; + + bpo::store( bpo::command_line_parser( opts ) + .options( subcommand_options ) + .positional( pos ) + .run(), + vm ); + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + pm.initialization_skipped = skip_initialize; + + return pm; + } + + protected: + bpo::positional_options_description _get_positional_options( bpo::options_description& options, + positional_descriptions& pos_desc )const + { + const T& derived = static_cast( *this ); + return derived.get_positional_options( options, pos_desc ); + } + }; + + template + class subcommand + : public subcommand_base + { + protected: + using base_type = subcommand_base; + + public: + static constexpr bool has_subcommands = false; + + + parse_metadata parse( const std::vector& opts, bool skip_initialize, + const std::vector& subcommand_context = std::vector() ) + { + parse_metadata pm( subcommand_context ); + pm.subcommand_desc = base_type::_get_description(); + pm.opts_description = std::make_shared( + base_type::_get_options(subcommand_context) ); + + bpo::variables_map vm; + + bpo::store( bpo::command_line_parser( opts ) + .options( *pm.opts_description ) + .run(), + vm ); + + if( !skip_initialize ) { + bpo::notify( vm ); + base_type::_initialize( std::move(vm) ); + } + pm.initialization_skipped = skip_initialize; + + return pm; + } + }; + + } /// namespace detail + + template + using subcommand = eosio::cli_parser::detail::subcommand; + + template + bpo::positional_options_description + build_positional_descriptions( const std::array& pos_names, + const bpo::options_description& options, + positional_descriptions& pos_desc ) + { + bpo::positional_options_description pos; + + for( std::size_t i = 0; i < pos_names.size(); ++i ) { + pos.add( pos_names[i].c_str(), 1 ); + const bpo::option_description* opt = options.find_nothrow( pos_names[i], false ); + if( opt ) { + pos_desc.emplace_back( pos_names[i], opt->description() ); + } else { + pos_desc.emplace_back( pos_names[i], "" ); + } + } + + return pos; + } + + void print_subcommand_help( std::ostream& stream, const std::string& program_name, const parse_metadata& pm ) { + const subcommand_descriptions* subcommands = std::get_if( &pm.other_description ); + bool subcommands_present = (subcommands && subcommands->size() > 0); + + const positional_descriptions* positionals = std::get_if( &pm.other_description ); + bool positionals_present = (positionals && positionals->size() > 0); + + auto formatting_width = pm.opts_description->get_option_column_width() - 2; + + std::string subcommand_usage; + for( const auto& s : pm.subcommand_context ) { + subcommand_usage += " "; + subcommand_usage += s; + } + if( subcommands_present ) { + subcommand_usage += " SUBCOMMAND"; + } + + if( pm.subcommand_desc.size() > 0 ) { + stream << pm.subcommand_desc << std::endl; + } + + stream << "Usage: " << program_name + << subcommand_usage + << " [OPTIONS]"; + + if( positionals_present ) { + for( const auto& p : *positionals ) { + stream << " " << p.positional_name; + } + } + + stream << std::endl << std::endl; + + if( positionals_present ) { + stream << "Positionals:" << std::endl; + + for( const auto& p : *positionals ) { + stream << " "; + auto orig_width = stream.width( formatting_width ); + stream << std::left << p.positional_name; + stream.width( orig_width ); + stream << p.description << std::endl; + } + + stream << std::endl; + } + + pm.opts_description->print( stream ); + + if( subcommands_present ) { + stream << std::endl; + + std::string subcommands_title; + if( pm.subcommand_context.size() > 0 ) { + for( const auto& s : pm.subcommand_context ) { + subcommands_title += s; + subcommands_title += " "; + } + subcommands_title += "subcommands:"; + } else { + subcommands_title = "Subcommands:"; + } + + stream << subcommands_title << std::endl; + for( const auto& s : *subcommands ) { + stream << " "; + auto orig_width = stream.width( formatting_width ); + stream << std::left << s.subcommand_name; + stream.width( orig_width ); + stream << s.description << std::endl; + } + } + } + + bool print_autocomplete_suggestions( std::ostream& stream, const parse_metadata& pm, const std::string& last_token ) { + bool printed_something = false; + + if( std::holds_alternative( pm.other_description ) ) { + for( const auto& s : std::get( pm.other_description ) ) { + if( s.subcommand_name.find( last_token ) != 0 ) continue; + if( printed_something ) + stream << " "; + stream << s.subcommand_name; + printed_something = true; + } + } + + if( last_token.size() > 0 ) { + for( const auto& opt : pm.opts_description->options() ) { + //if( opt->match( last_token, true, false, false ) == bpo::option_description::no_match ) continue; + // The option_description::match function doesn't seem to work. + + const auto& short_name = opt->canonical_display_name( bpo::command_line_style::allow_dash_for_short ); + if( short_name.size() > 0 && short_name[0] == '-' && short_name.find( last_token ) == 0 ) { + if( printed_something ) + stream << " "; + stream << short_name; + printed_something = true; + } + + const auto& long_name = opt->canonical_display_name( bpo::command_line_style::allow_long ); + if( long_name.size() > 0 && long_name[0] == '-' && long_name.find( last_token ) == 0 ) { + if( printed_something ) + stream << " "; + stream << long_name; + printed_something = true; + } + } + } + + return printed_something; + } + + template + std::optional + parse_program_options( T& root, const std::vector& opts, + bool skip_initialize = false, bool avoid_throwing = false ) + { + try { + return root.parse( opts, skip_initialize ); + } catch( ... ) { + if( !avoid_throwing ) throw; + } + + return {}; + } + + template + std::variant + parse_program_options_extended( T& root, int argc, const char** argv, + std::ostream& out_stream, std::ostream* err_stream = nullptr, + bool avoid_throwing = false ) + { + if( argc <= 0 ) { + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "command line arguments should at least include program name" ); + } + + std::string program_name{ bfs::path( argv[0] ).filename().generic_string() }; + + bool autocomplete_mode = false; + + std::vector opts; + opts.reserve( argc - 1 ); + for( int i = 1; i < argc; ++i ) { + opts.emplace_back( std::string( *(argv + i) ) ); + if( opts.back().compare( "-" ) == 0 ) { + if( autocomplete_mode ) return autocomplete_failure{}; + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "- is not allowed by itself in the command line arguments" ); + } + if( opts.back().compare( "--" ) == 0 ) { + if( i != 1 ) { + if( autocomplete_mode ) return autocomplete_failure{}; + if( avoid_throwing ) return parse_failure{}; + FC_ASSERT( false, "-- is not allowed by itself in the command line arguments" ); + } + opts.pop_back(); + autocomplete_mode = true; + } + } + + if( !autocomplete_mode ) { + const auto pm = parse_program_options( root, opts, false, avoid_throwing ); + if( !pm ) return parse_failure{}; + return *pm; + } + + // Autocomplete helper logic: + + std::string last_token; + if( opts.size() > 0 ) { + if( opts.back().size() == 0 ) { + opts.pop_back(); + } else { + last_token = opts.back(); + } + } + + if( last_token.size() > 0 ) { + if( opts.size() == 0 ) return autocomplete_failure{}; + opts.pop_back(); + } + + auto pm = parse_program_options( root, opts, true, true ); + if( !pm ) return autocomplete_failure{}; + + if( last_token.size() > 0 ) { + if( print_autocomplete_suggestions( out_stream, *pm, last_token ) ) { + out_stream << std::endl; + } else { + return autocomplete_failure{}; + } + } + + if( err_stream ) { + *err_stream << std::endl; + print_subcommand_help( *err_stream, program_name, *pm ); + } + + return autocomplete_success{}; + } + + enum class dispatch_status { + successful_dispatch, + no_handler_for_terminal_subcommand, + no_handler_for_nonterminal_subcommand + }; + + namespace detail { + + template + auto dispatch_helper( Reducer&& reduce, Visitor&& visitor, const LastArg& last_arg, const OtherArgs&... other_args ) + -> std::enable_if_t< + !std::is_same_v, no_subcommand>, + std::pair + > + { + static_assert( std::is_convertible_v>, Ret> ); + + dispatch_status status = dispatch_status::no_handler_for_terminal_subcommand; + + if constexpr( LastArg::has_subcommands ) { + return std::visit([&]( const auto& next_subcommand ) { + return dispatch_helper( std::forward(reduce), + std::forward(visitor), + next_subcommand, + other_args..., last_arg ); + }, last_arg.get_subcommand() ); + } else { + if( maybe_call_visitor( std::forward(visitor), other_args..., last_arg ) ) + status = dispatch_status::successful_dispatch; + } + + return {status, reduce( std::make_tuple( std::cref(other_args)..., std::cref(last_arg) ) )}; + } + + template + auto dispatch_helper( Reducer&& reduce, Visitor&& visitor, const LastArg& last_arg, const OtherArgs&... other_args ) + -> std::enable_if_t< + std::is_same_v, no_subcommand>, + std::pair + > + { + static_assert( std::is_convertible_v>, Ret> ); + + dispatch_status status = dispatch_status::no_handler_for_nonterminal_subcommand; + + if( maybe_call_visitor( std::forward(visitor), other_args... ) ) + status = dispatch_status::successful_dispatch; + + return {status, reduce( std::make_tuple( std::cref(other_args)... ) )}; + } + + template + std::string get_subcommand_string( const std::tuple& t ) { + static_assert( (has_name::value && ...) ); + + std::string result; + + bool completed = reduce_tuple( result, t, + []( auto&& v ) -> std::string { return std::decay_t::name; }, + []( std::string& accumulator, std::size_t index, const std::string& v ) -> bool { + if( index != 0 ) { + if( index > 1 ) { + accumulator += " "; + } + accumulator += v; + } + return true; + } + ); + + if( !completed ) return {}; + + return result; + } + + } /// namespace detail + + template + auto dispatch( Dispatcher&& dispatcher, const Root& root ) -> std::pair { + return detail::dispatch_helper( + []( const auto& t ) { return detail::get_subcommand_string(t); }, + std::forward(dispatcher), + root + ); + } + +} /// namespace eosio::cli_parser + +namespace eosio { + using subcommand_style = cli_parser::subcommand_style; + + template + using subcommand = eosio::cli_parser::subcommand; +} diff --git a/programs/intrinsic-log-util/main.cpp b/programs/intrinsic-log-util/main.cpp index 7089157c70f..7636c782e37 100644 --- a/programs/intrinsic-log-util/main.cpp +++ b/programs/intrinsic-log-util/main.cpp @@ -2,7 +2,7 @@ * @file * @copyright defined in eosio/LICENSE.txt */ -#include "subcommand.hpp" +#include "cli_parser.hpp" #include #include @@ -14,6 +14,7 @@ #include #include +#include using namespace eosio; using namespace eosio::chain; @@ -48,7 +49,7 @@ struct print_block_subcommand } bpo::positional_options_description - get_positional_options( bpo::options_description& options, positional_descriptions& pos_desc )const { + get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc )const { options.add_options() ("block-num", bpo::value(), "the block number to print") @@ -58,7 +59,7 @@ struct print_block_subcommand "block-num" }; - return build_positional_descriptions( pos_args, options, pos_desc ); + return cli_parser::build_positional_descriptions( pos_args, options, pos_desc ); } }; @@ -141,7 +142,7 @@ struct diff_subcommand } bpo::positional_options_description - get_positional_options( bpo::options_description& options, positional_descriptions& pos_desc )const { + get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc )const { options.add_options() ("first-log", bpo::value(), "path to the first log file to compare") @@ -154,7 +155,7 @@ struct diff_subcommand "second-log" }; - return build_positional_descriptions( pos_args, options, pos_desc ); + return cli_parser::build_positional_descriptions( pos_args, options, pos_desc ); } }; @@ -182,29 +183,64 @@ struct root_command bool print_help = false; }; +class subcommand_dispatcher { +public: + subcommand_dispatcher( std::ostream& stream ) + :stream( stream ) + {} + + void operator()( const root_command& root, const diff_subcommand& diff ) { + stream << "diff was called" << std::endl; + } + + void operator()( const root_command& root, const print_subcommand& print, const print_blocks_subcommand& blocks ) { + stream << "print blocks was called" << std::endl; + } + +protected: + std::ostream& stream; +}; + int main( int argc, const char** argv ) { try { root_command rc; - auto res = parse_program_options_extended( rc, argc, argv, std::cout, &std::cerr ); + auto parse_result = cli_parser::parse_program_options_extended( rc, argc, argv, std::cout, &std::cerr ); - if( std::holds_alternative( res ) ) { + if( std::holds_alternative( parse_result ) ) { FC_ASSERT( false, "parse error occurred" ); - } else if( std::holds_alternative( res ) ) { + } else if( std::holds_alternative( parse_result ) ) { return -1; - } else if( std::holds_alternative( res ) ) { + } else if( std::holds_alternative( parse_result ) ) { return 0; } - const auto& pm = std::get( res ); + const auto& pm = std::get( parse_result ); if( argc == 1 || rc.print_help ) { - print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); + cli_parser::print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); return 0; } std::ios::sync_with_stdio(false); // for potential performance boost for large log files - + subcommand_dispatcher d( std::cout ); + auto dispatch_result = cli_parser::dispatch( d, rc ); + + FC_ASSERT( dispatch_result.first != cli_parser::dispatch_status::no_handler_for_terminal_subcommand, + "The ${subcommand} sub-command has not been implemented yet.", + ("subcommand", dispatch_result.second) + ); + + if( dispatch_result.first == cli_parser::dispatch_status::no_handler_for_nonterminal_subcommand ) { + std::cout << "\033[0;31m" + << "The sub-command " + << "\033[1;31m" << dispatch_result.second << "\033[0;31m" + << " cannot be called by itself." + << "\033[0m" + << std::endl; + cli_parser::print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); + return -1; + } } catch( const fc::exception& e ) { elog( "${e}", ("e", e.to_detail_string())); diff --git a/programs/intrinsic-log-util/subcommand.hpp b/programs/intrinsic-log-util/subcommand.hpp deleted file mode 100644 index 8b2293f47fe..00000000000 --- a/programs/intrinsic-log-util/subcommand.hpp +++ /dev/null @@ -1,651 +0,0 @@ -/** - * @file - * @copyright defined in eosio/LICENSE.txt - */ -#pragma once - -#include - -#include -#include - -#include -#include -#include - -//#include -#include -#include -#include - -namespace eosio { - -namespace bfs = boost::filesystem; -namespace bpo = boost::program_options; - -enum class subcommand_style { - contains_subcommands, - terminal_subcommand_with_positional_arguments, - terminal_subcommand_without_positional_arguments -}; - -namespace detail { - template struct overloaded : Ts... { using Ts::operator()...; }; - template overloaded(Ts...) -> overloaded; - - template - struct has_name : std::false_type {}; - - template - struct has_name : std::true_type {}; - - template - struct has_description : std::false_type {}; - - template - struct has_description : std::true_type {}; - - template - struct has_initialize : std::false_type {}; - - template - struct has_initialize().initialize( std::move( std::declval() ) ), 0)> - : std::true_type - {}; - - template - bool visit_by_name_helper( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { - using variant_type = std::variant; - - if constexpr( I < std::variant_size_v ) - { - if constexpr( has_name< std::variant_alternative_t >::value ) { - if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { - std::forward(visitor)( std::get( var ) ); - return true; - } - } - return visit_by_name_helper( var, - subcommand_name, - std::forward(visitor) ); - } else { - return false; - } - } - - template - bool visit_by_name( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { - return visit_by_name_helper<0>( var, subcommand_name, std::forward( visitor ) ); - } - - template - bool construct_variant_by_name_helper( Variant& var, - const std::string& subcommand_name, - Visitor&& visitor, - Args&&... args ) - { - if constexpr( I < std::variant_size_v ) { - if constexpr( has_name< std::variant_alternative_t >::value ) { - if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { - std::forward(visitor)( var.template emplace( std::forward(args)... ) ); - return true; - } - } - return construct_variant_by_name_helper( var, - subcommand_name, - std::forward(visitor), - std::forward(args)... ); - } else { - return false; - } - } - - template - bool construct_variant_by_name( Variant& var, - const std::string& subcommand_name, - Args&&... args ) - { - return construct_variant_by_name_helper<0>( var, - subcommand_name, - []( auto&& ) {}, // do nothing in visitor - std::forward(args)... ); - } - - template - bool construct_and_visit_variant_by_name( Variant& var, - const std::string& subcommand_name, - Visitor&& visitor, - Args&&... args ) - { - return construct_variant_by_name_helper<0>( var, - subcommand_name, - std::forward(visitor), - std::forward(args)... ); - } - - template - void static_visit_all_helper( Visitor&& visitor ) - { - if constexpr( I < std::variant_size_v ) { - if constexpr( has_name< std::variant_alternative_t >::value ) { - std::string desc; - if constexpr( has_description< std::variant_alternative_t >::value ) { - desc = std::variant_alternative_t::get_description(); - } - visitor( std::variant_alternative_t::name, desc ); - } - return static_visit_all_helper( std::forward(visitor) ); - } - } - - template - void static_visit_all( Visitor&& visitor ) { - static_visit_all_helper<0, Variant>( std::forward(visitor) ); - } - - struct no_subcommand {}; - - class subcommand_untyped_base {}; - - template - class subcommand_base : public subcommand_untyped_base { - static_assert( (std::is_default_constructible_v && ...) ); - static_assert( (std::is_base_of_v && ...), "subcommands must derive from subcommand type" ); - - protected: - subcommand_base() = default; - friend T; - - bpo::options_description _get_options( const std::vector& subcommand_context ) - { - std::string options_name; - if( subcommand_context.size() > 0 ) { - for( const auto& s : subcommand_context ) { - options_name += s; - options_name += " "; - } - } else { - options_name += T::name; - options_name += " "; - } - options_name += "options"; - - bpo::options_description options( options_name ); - T& derived = static_cast( *this ); - derived.set_options( options ); - return options; - } - - template - static std::string _get_description() { - if constexpr( detail::has_description::value ) { - return U::get_description(); - } else { - return {}; - } - } - - template - void _initialize( bpo::variables_map&& vm ) { - if constexpr( detail::has_initialize::value ) { - T& derived = static_cast( *this ); - derived.initialize( std::move(vm) ); - } - } - }; - -} /// namespace eosio::detail - -struct subcommand_description { - subcommand_description( const std::string& subcommand_name, const std::string& description ) - :subcommand_name( subcommand_name ) - ,description( description ) - {} - - std::string subcommand_name; - std::string description; -}; - -using subcommand_descriptions = std::vector; - -struct positional_description { - positional_description( const std::string& positional_name, const std::string& description ) - :positional_name( positional_name ) - ,description( description ) - {} - - std::string positional_name; - std::string description; -}; - -using positional_descriptions = std::vector; - -struct parse_metadata { - parse_metadata( const std::vector& context ) - :subcommand_context( context ) - {} - - /** @pre subcommand_descriptions alternative of other_description must be present */ - void add_subcommand_description( const std::string& subcommand, const std::string& description ) - { - std::get( other_description ).emplace_back( subcommand, description ); - } - - void set_positional_descriptions( const positional_descriptions& pos_desc ) { - other_description.emplace( pos_desc ); - } - - void set_positional_descriptions( positional_descriptions&& pos_desc ) { - other_description.emplace( std::move(pos_desc) ); - } - - std::vector subcommand_context; - std::string subcommand_desc; - std::shared_ptr opts_description; - std::variant other_description; - bool initialization_skipped = false; -}; - -template -class subcommand; - -template -class subcommand - : public detail::subcommand_base -{ -protected: - using base_type = detail::subcommand_base; - using subcommands_type = std::variant; - - subcommands_type _subcommand; - -public: - - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - bpo::options_description subcommand_options{base_type::_get_options(subcommand_context)}; - - auto subcommand_options_ptr = std::make_shared( subcommand_options ); - - subcommand_options.add_options() - ("__command", bpo::value(), "command to execute" ) - ("__subargs", bpo::value>(), "arguments for command" ) - ; - - bpo::positional_options_description pos; - pos.add( "__command", 1 ).add( "__subargs", -1 ); - - bpo::parsed_options parsed = bpo::command_line_parser( opts ) - .options( subcommand_options ) - .positional( pos ) - .allow_unregistered() - .run(); - - bpo::variables_map vm; - - bpo::variables_map vm_temp; - bpo::store( parsed, vm_temp ); - - if( vm_temp.count( "__command" ) > 0 ) { - std::string cmd_name = vm_temp["__command"].as(); - - std::vector inner_opts = bpo::collect_unrecognized( parsed.options, bpo::include_positional ); - FC_ASSERT( inner_opts.size() > 0, "unexpected error" ); - FC_ASSERT( *inner_opts.begin() == cmd_name, - "unrecognized option '${option}'", - ("option", *inner_opts.begin()) - ); - inner_opts.erase( inner_opts.begin() ); - - bool success = detail::construct_and_visit_variant_by_name( _subcommand, cmd_name, - [&inner_opts, &pm, &cmd_name, &subcommand_options_ptr, &skip_initialize, context = subcommand_context] - ( auto&& s ) mutable { - context.emplace_back( cmd_name ); - pm = s.parse( inner_opts, skip_initialize, context ); - FC_ASSERT( pm.opts_description, "unexpected error" ); - subcommand_options_ptr->add( *pm.opts_description ); - pm.opts_description = subcommand_options_ptr; - } - ); - FC_ASSERT( success, "unrecognized sub-command '${name}' within context: ${context}", - ("name", cmd_name)("context", subcommand_context) - ); - vm = std::move(vm_temp); - } else { - bpo::parsed_options parsed2 = bpo::command_line_parser( opts ) - .options( *subcommand_options_ptr ) - .run(); - bpo::store( parsed2, vm ); - - detail::static_visit_all>( - [&pm]( const char* name, const std::string& desc ) { - pm.add_subcommand_description( name, desc ); - } - ); - - pm.opts_description = std::move(subcommand_options_ptr); - pm.subcommand_desc = base_type::_get_description(); - pm.initialization_skipped = skip_initialize; - } - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - - return pm; - } - - const subcommands_type& get_subcommand()const { return _subcommand; } -}; - -template -class subcommand - : public detail::subcommand_base -{ -protected: - using base_type = detail::subcommand_base; - -public: - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - pm.subcommand_desc = base_type::_get_description(); - pm.opts_description = std::make_shared( base_type::_get_options(subcommand_context) ); - - bpo::options_description subcommand_options{ *pm.opts_description }; - positional_descriptions pos_desc; - const auto& pos = _get_positional_options( subcommand_options, pos_desc ); - pm.set_positional_descriptions( pos_desc ); - - bpo::variables_map vm; - - bpo::store( bpo::command_line_parser( opts ) - .options( subcommand_options ) - .positional( pos ) - .run(), - vm ); - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - pm.initialization_skipped = skip_initialize; - - return pm; - } - -protected: - bpo::positional_options_description _get_positional_options( bpo::options_description& options, - positional_descriptions& pos_desc )const - { - const T& derived = static_cast( *this ); - return derived.get_positional_options( options, pos_desc ); - } -}; - -template -class subcommand - : public detail::subcommand_base -{ -protected: - using base_type = detail::subcommand_base; - -public: - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - pm.subcommand_desc = base_type::_get_description(); - pm.opts_description = std::make_shared( base_type::_get_options(subcommand_context) ); - - bpo::variables_map vm; - - bpo::store( bpo::command_line_parser( opts ) - .options( *pm.opts_description ) - .run(), - vm ); - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - pm.initialization_skipped = skip_initialize; - - return pm; - } -}; - -template -bpo::positional_options_description -build_positional_descriptions( const std::array& pos_names, - const bpo::options_description& options, - positional_descriptions& pos_desc ) -{ - bpo::positional_options_description pos; - - for( std::size_t i = 0; i < pos_names.size(); ++i ) { - pos.add( pos_names[i].c_str(), 1 ); - const bpo::option_description* opt = options.find_nothrow( pos_names[i], false ); - if( opt ) { - pos_desc.emplace_back( pos_names[i], opt->description() ); - } else { - pos_desc.emplace_back( pos_names[i], "" ); - } - } - - return pos; -} - -void print_subcommand_help( std::ostream& stream, const std::string& program_name, const parse_metadata& pm ) { - const subcommand_descriptions* subcommands = std::get_if( &pm.other_description ); - bool subcommands_present = (subcommands && subcommands->size() > 0); - - const positional_descriptions* positionals = std::get_if( &pm.other_description ); - bool positionals_present = (positionals && positionals->size() > 0); - - auto formatting_width = pm.opts_description->get_option_column_width() - 2; - - std::string subcommand_usage; - for( const auto& s : pm.subcommand_context ) { - subcommand_usage += " "; - subcommand_usage += s; - } - if( subcommands_present ) { - subcommand_usage += " SUBCOMMAND"; - } - - if( pm.subcommand_desc.size() > 0 ) { - stream << pm.subcommand_desc << std::endl; - } - - stream << "Usage: " << program_name - << subcommand_usage - << " [OPTIONS]"; - - if( positionals_present ) { - for( const auto& p : *positionals ) { - stream << " " << p.positional_name; - } - } - - stream << std::endl << std::endl; - - if( positionals_present ) { - stream << "Positionals:" << std::endl; - - for( const auto& p : *positionals ) { - stream << " "; - auto orig_width = stream.width( formatting_width ); - stream << std::left << p.positional_name; - stream.width( orig_width ); - stream << p.description << std::endl; - } - - stream << std::endl; - } - - pm.opts_description->print( stream ); - - if( subcommands_present ) { - stream << std::endl; - - std::string subcommands_title; - if( pm.subcommand_context.size() > 0 ) { - for( const auto& s : pm.subcommand_context ) { - subcommands_title += s; - subcommands_title += " "; - } - subcommands_title += "subcommands:"; - } else { - subcommands_title = "Subcommands:"; - } - - stream << subcommands_title << std::endl; - for( const auto& s : *subcommands ) { - stream << " "; - auto orig_width = stream.width( formatting_width ); - stream << std::left << s.subcommand_name; - stream.width( orig_width ); - stream << s.description << std::endl; - } - } -} - -bool print_autocomplete_suggestions( std::ostream& stream, const parse_metadata& pm, const std::string& last_token ) { - bool printed_something = false; - - if( std::holds_alternative( pm.other_description ) ) { - for( const auto& s : std::get( pm.other_description ) ) { - if( s.subcommand_name.find( last_token ) != 0 ) continue; - if( printed_something ) - stream << " "; - stream << s.subcommand_name; - printed_something = true; - } - } - - if( last_token.size() > 0 ) { - for( const auto& opt : pm.opts_description->options() ) { - //if( opt->match( last_token, true, false, false ) == bpo::option_description::no_match ) continue; - // The option_description::match function doesn't seem to work. - - const auto& short_name = opt->canonical_display_name( bpo::command_line_style::allow_dash_for_short ); - if( short_name.size() > 0 && short_name[0] == '-' && short_name.find( last_token ) == 0 ) { - if( printed_something ) - stream << " "; - stream << short_name; - printed_something = true; - } - - const auto& long_name = opt->canonical_display_name( bpo::command_line_style::allow_long ); - if( long_name.size() > 0 && long_name[0] == '-' && long_name.find( last_token ) == 0 ) { - if( printed_something ) - stream << " "; - stream << long_name; - printed_something = true; - } - } - } - - return printed_something; -} - -template -std::optional -parse_program_options( T& root, const std::vector& opts, //int argc, const char** argv, - bool skip_initialize = false, bool avoid_throwing = false ) -{ - try { - return root.parse( opts, skip_initialize ); - } catch( ... ) { - if( !avoid_throwing ) throw; - } - - return {}; -} - -struct autocomplete_failure {}; -struct autocomplete_success {}; -struct parse_failure {}; - -template -std::variant -parse_program_options_extended( T& root, int argc, const char** argv, - std::ostream& out_stream, std::ostream* err_stream = nullptr, - bool avoid_throwing = false ) -{ - if( argc <= 0 ) { - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "command line arguments should at least include program name" ); - } - - std::string program_name{ bfs::path( argv[0] ).filename().generic_string() }; - - bool autocomplete_mode = false; - - std::vector opts; - opts.reserve( argc - 1 ); - for( int i = 1; i < argc; ++i ) { - opts.emplace_back( std::string( *(argv + i) ) ); - if( opts.back().compare( "-" ) == 0 ) { - if( autocomplete_mode ) return autocomplete_failure{}; - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "- is not allowed by itself in the command line arguments" ); - } - if( opts.back().compare( "--" ) == 0 ) { - if( i != 1 ) { - if( autocomplete_mode ) return autocomplete_failure{}; - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "-- is not allowed by itself in the command line arguments" ); - } - opts.pop_back(); - autocomplete_mode = true; - } - } - - if( !autocomplete_mode ) { - const auto pm = parse_program_options( root, opts, false, avoid_throwing ); - if( !pm ) return parse_failure{}; - return *pm; - } - - // Autocomplete helper logic: - - std::string last_token; - if( opts.size() > 0 ) { - if( opts.back().size() == 0 ) { - opts.pop_back(); - } else { - last_token = opts.back(); - } - } - - if( last_token.size() > 0 ) { - if( opts.size() == 0 ) return autocomplete_failure{}; - opts.pop_back(); - } - - auto pm = parse_program_options( root, opts, true, true ); - if( !pm ) return autocomplete_failure{}; - - if( last_token.size() > 0 ) { - if( print_autocomplete_suggestions( out_stream, *pm, last_token ) ) { - out_stream << std::endl; - } else { - return autocomplete_failure{}; - } - } - - if( err_stream ) { - *err_stream << std::endl; - print_subcommand_help( *err_stream, program_name, *pm ); - } - - return autocomplete_success{}; -} - -} /// namespace eosio From 640f2331d175d30d21503842dcd51a447bc6aaba Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 29 May 2019 19:47:45 -0400 Subject: [PATCH 28/88] add support for optional and repeating positional arguments; some sub-commands containing nested sub-commands are invokable by themselves while others are not --- programs/intrinsic-log-util/cli_parser.hpp | 98 ++++++++++++++++------ programs/intrinsic-log-util/main.cpp | 59 +++++++------ 2 files changed, 108 insertions(+), 49 deletions(-) diff --git a/programs/intrinsic-log-util/cli_parser.hpp b/programs/intrinsic-log-util/cli_parser.hpp index e97a279909b..9d3f793ef76 100644 --- a/programs/intrinsic-log-util/cli_parser.hpp +++ b/programs/intrinsic-log-util/cli_parser.hpp @@ -22,7 +22,8 @@ namespace eosio::cli_parser { namespace bpo = boost::program_options; enum class subcommand_style { - contains_subcommands, + only_contains_subcommands, + invokable_and_contains_subcommands, terminal_subcommand_with_positional_arguments, terminal_subcommand_without_positional_arguments }; @@ -37,16 +38,29 @@ namespace eosio::cli_parser { std::string description; }; - using subcommand_descriptions = std::vector; + struct subcommand_descriptions { + std::vector descriptions; + bool requires_subcommand = false; + }; struct positional_description { - positional_description( const std::string& positional_name, const std::string& description ) + enum argument_restriction { + required, + optional, + repeating + }; + + positional_description( const std::string& positional_name, + const std::string& description, + argument_restriction restriction ) :positional_name( positional_name ) ,description( description ) + ,restriction( restriction ) {} - std::string positional_name; - std::string description; + std::string positional_name; + std::string description; + argument_restriction restriction; }; using positional_descriptions = std::vector; @@ -63,7 +77,7 @@ namespace eosio::cli_parser { /** @pre subcommand_descriptions alternative of other_description must be present */ void add_subcommand_description( const std::string& subcommand, const std::string& description ) { - std::get( other_description ).emplace_back( subcommand, description ); + std::get( other_description ).descriptions.emplace_back( subcommand, description ); } void set_positional_descriptions( const positional_descriptions& pos_desc ) { @@ -284,20 +298,26 @@ namespace eosio::cli_parser { template class subcommand; - template - class subcommand + template + class subcommand : public subcommand_base { protected: using base_type = subcommand_base; using subcommands_type = std::variant; + using _enable = std::enable_if_t< + SubcommandStyle == subcommand_style::only_contains_subcommands + || SubcommandStyle == subcommand_style::invokable_and_contains_subcommands + >; + static_assert( sizeof...(Us) > 0 ); subcommands_type _subcommand; public: - static constexpr bool has_subcommands = true; + static constexpr bool has_subcommands = true; + static constexpr bool invokable_subcommand = (SubcommandStyle == subcommand_style::invokable_and_contains_subcommands); parse_metadata parse( const std::vector& opts, bool skip_initialize, const std::vector& subcommand_context = std::vector() ) @@ -357,6 +377,8 @@ namespace eosio::cli_parser { .run(); bpo::store( parsed2, vm ); + std::get( pm.other_description ).requires_subcommand = !invokable_subcommand; + static_visit_all>( [&pm]( const char* name, const std::string& desc ) { pm.add_subcommand_description( name, desc ); @@ -388,6 +410,7 @@ namespace eosio::cli_parser { public: static constexpr bool has_subcommands = false; + static constexpr bool invokable_subcommand = true; parse_metadata parse( const std::vector& opts, bool skip_initialize, const std::vector& subcommand_context = std::vector() ) @@ -421,9 +444,9 @@ namespace eosio::cli_parser { protected: bpo::positional_options_description _get_positional_options( bpo::options_description& options, - positional_descriptions& pos_desc )const + positional_descriptions& pos_desc ) { - const T& derived = static_cast( *this ); + T& derived = static_cast( *this ); return derived.get_positional_options( options, pos_desc ); } }; @@ -437,6 +460,7 @@ namespace eosio::cli_parser { public: static constexpr bool has_subcommands = false; + static constexpr bool invokable_subcommand = true; parse_metadata parse( const std::vector& opts, bool skip_initialize, @@ -469,22 +493,38 @@ namespace eosio::cli_parser { template using subcommand = eosio::cli_parser::detail::subcommand; - template bpo::positional_options_description - build_positional_descriptions( const std::array& pos_names, - const bpo::options_description& options, - positional_descriptions& pos_desc ) + build_positional_descriptions( const bpo::options_description& options, + positional_descriptions& pos_desc, + const std::vector& required_pos_names, + const std::vector& optional_pos_names = std::vector(), + const std::string& repeat_name = std::string{} + ) { bpo::positional_options_description pos; - for( std::size_t i = 0; i < pos_names.size(); ++i ) { - pos.add( pos_names[i].c_str(), 1 ); - const bpo::option_description* opt = options.find_nothrow( pos_names[i], false ); + auto add_positional = [&pos, &pos_desc, &options]( const std::string& pos_name, + positional_description::argument_restriction restriction ) + { + pos.add( pos_name.c_str(), (restriction == positional_description::repeating ? -1 : 1) ); + const bpo::option_description* opt = options.find_nothrow( pos_name, false ); if( opt ) { - pos_desc.emplace_back( pos_names[i], opt->description() ); + pos_desc.emplace_back( pos_name, opt->description(), restriction ); } else { - pos_desc.emplace_back( pos_names[i], "" ); + pos_desc.emplace_back( pos_name, "", restriction ); } + }; + + for( const auto& n : required_pos_names ) { + add_positional( n, positional_description::required ); + } + + for( const auto& n : optional_pos_names ) { + add_positional( n, positional_description::optional ); + } + + if( repeat_name.size() > 0 ) { + add_positional( repeat_name, positional_description::repeating ); } return pos; @@ -492,7 +532,7 @@ namespace eosio::cli_parser { void print_subcommand_help( std::ostream& stream, const std::string& program_name, const parse_metadata& pm ) { const subcommand_descriptions* subcommands = std::get_if( &pm.other_description ); - bool subcommands_present = (subcommands && subcommands->size() > 0); + bool subcommands_present = (subcommands && subcommands->descriptions.size() > 0); const positional_descriptions* positionals = std::get_if( &pm.other_description ); bool positionals_present = (positionals && positionals->size() > 0); @@ -505,7 +545,10 @@ namespace eosio::cli_parser { subcommand_usage += s; } if( subcommands_present ) { - subcommand_usage += " SUBCOMMAND"; + if( subcommands->requires_subcommand ) + subcommand_usage += " SUBCOMMAND"; + else + subcommand_usage += " [SUBCOMMAND]"; } if( pm.subcommand_desc.size() > 0 ) { @@ -518,7 +561,12 @@ namespace eosio::cli_parser { if( positionals_present ) { for( const auto& p : *positionals ) { - stream << " " << p.positional_name; + bool optional_arg = (p.restriction == positional_description::optional); + stream << " "; + if( optional_arg ) stream << "["; + stream << p.positional_name; + if( optional_arg ) stream << "]"; + if( p.restriction == positional_description::repeating ) stream << "..."; } } @@ -555,7 +603,7 @@ namespace eosio::cli_parser { } stream << subcommands_title << std::endl; - for( const auto& s : *subcommands ) { + for( const auto& s : subcommands->descriptions ) { stream << " "; auto orig_width = stream.width( formatting_width ); stream << std::left << s.subcommand_name; @@ -569,7 +617,7 @@ namespace eosio::cli_parser { bool printed_something = false; if( std::holds_alternative( pm.other_description ) ) { - for( const auto& s : std::get( pm.other_description ) ) { + for( const auto& s : std::get( pm.other_description ).descriptions ) { if( s.subcommand_name.find( last_token ) != 0 ) continue; if( printed_something ) stream << " "; diff --git a/programs/intrinsic-log-util/main.cpp b/programs/intrinsic-log-util/main.cpp index 7636c782e37..b8d432cdc34 100644 --- a/programs/intrinsic-log-util/main.cpp +++ b/programs/intrinsic-log-util/main.cpp @@ -4,6 +4,7 @@ */ #include "cli_parser.hpp" #include +#include #include #include @@ -41,7 +42,7 @@ struct print_block_subcommand return "Print a specific block"; } - void set_options( bpo::options_description& options )const { + void set_options( bpo::options_description& options ) { options.add_options() ("output-file,o", bpo::value(), "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") @@ -49,18 +50,29 @@ struct print_block_subcommand } bpo::positional_options_description - get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc )const { + get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc ) { options.add_options() - ("block-num", bpo::value(), + ("block-num", bpo::value(&block_num), "the block number to print") + ("trx-id", bpo::value>(), + "ID of transaction to print (optional)") ; - std::array pos_args = { - "block-num" - }; + return cli_parser::build_positional_descriptions( options, pos_desc, { "block-num" }, {}, "trx-id" ); + } - return cli_parser::build_positional_descriptions( pos_args, options, pos_desc ); + void initialize( bpo::variables_map&& vm ) { + if( vm.count( "trx-id" ) ) { + for( const auto& trx_id : vm.at( "trx-id" ).as>() ) { + try { + trxs_filter.emplace_back( fc::variant( trx_id ).as() ); + } EOS_RETHROW_EXCEPTIONS( fc::exception, "invalid transaction id: ${trx_id}", ("trx_id", trx_id) ) + } + } } + + uint32_t block_num = 0; + std::vector trxs_filter; }; struct print_blocks_subcommand @@ -75,11 +87,11 @@ struct print_blocks_subcommand return "Print a range of blocks"; } - void set_options( bpo::options_description& options )const { + void set_options( bpo::options_description& options ) { options.add_options() ("output-file,o", bpo::value(), "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") - ("first,f", bpo::value()->default_value(0), + ("first,f", bpo::value(&first_block_num)->default_value(0), "the first block number to print") ("last,l", bpo::value()->default_value(std::numeric_limits::max()), "the first block number to print") @@ -87,10 +99,6 @@ struct print_blocks_subcommand } void initialize( bpo::variables_map&& vm ) { - if( vm.count( "first" ) ) { - first_block_num = vm.at( "first" ).as(); - } - if( vm.count( "last" ) ) { last_block_num = vm.at( "last" ).as(); FC_ASSERT( last_block_num >= first_block_num, "invalid range" ); @@ -104,7 +112,7 @@ struct print_blocks_subcommand struct print_subcommand : public subcommand< print_subcommand, - subcommand_style::contains_subcommands, + subcommand_style::invokable_and_contains_subcommands, print_block_subcommand, print_blocks_subcommand > @@ -115,7 +123,7 @@ struct print_subcommand return "Print blocks in a log file"; } - void set_options( bpo::options_description& options )const { + void set_options( bpo::options_description& options ) { options.add_options() ("no-pretty-print", bpo::bool_switch()->default_value(false), "avoid pretty printing") ; @@ -134,7 +142,7 @@ struct diff_subcommand return "Find the differences between two log files"; } - void set_options( bpo::options_description& options )const { + void set_options( bpo::options_description& options ) { options.add_options() ("start-block", bpo::value()->default_value(0), "ignore any differences prior to this block") @@ -142,7 +150,7 @@ struct diff_subcommand } bpo::positional_options_description - get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc )const { + get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc ) { options.add_options() ("first-log", bpo::value(), "path to the first log file to compare") @@ -150,19 +158,16 @@ struct diff_subcommand "path to the second log file to compare") ; - std::array pos_args = { - "first-log", - "second-log" - }; - - return cli_parser::build_positional_descriptions( pos_args, options, pos_desc ); + return cli_parser::build_positional_descriptions( options, pos_desc, { + "first-log", "second-log" + } ); } }; struct root_command : public subcommand< root_command, - subcommand_style::contains_subcommands, + subcommand_style::only_contains_subcommands, print_subcommand, diff_subcommand > @@ -195,6 +200,12 @@ class subcommand_dispatcher { void operator()( const root_command& root, const print_subcommand& print, const print_blocks_subcommand& blocks ) { stream << "print blocks was called" << std::endl; + idump((blocks.first_block_num)(blocks.last_block_num)); + } + + void operator()( const root_command& root, const print_subcommand& print, const print_block_subcommand& block ) { + stream << "print block was called" << std::endl; + idump((block.block_num)(block.trxs_filter)); } protected: From 861efee6e17e19a8fcc02d2f49350a0fb19f28b9 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 17 Jun 2019 15:22:07 -0400 Subject: [PATCH 29/88] redoing eos-vm submodule --- libraries/eos-vm | 1 - 1 file changed, 1 deletion(-) delete mode 160000 libraries/eos-vm diff --git a/libraries/eos-vm b/libraries/eos-vm deleted file mode 160000 index 729ead80e28..00000000000 --- a/libraries/eos-vm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 729ead80e28e513d00a90066ea2948683486be5b From 2ec9e75e26ce4ebaa027c62276e2793a91fb7426 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 17 Jun 2019 15:24:15 -0400 Subject: [PATCH 30/88] added eos-vm submodule --- .gitmodules | 2 +- libraries/eos-vm | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 libraries/eos-vm diff --git a/.gitmodules b/.gitmodules index 4e25bc2f43c..bcf5fb4b178 100644 --- a/.gitmodules +++ b/.gitmodules @@ -20,4 +20,4 @@ url = https://github.com/Yubico/yubihsm-shell [submodule "libraries/eos-vm"] path = libraries/eos-vm - url = git@github.com:eosio/eos-vm + url = https://github.com/eosio/eos-vm diff --git a/libraries/eos-vm b/libraries/eos-vm new file mode 160000 index 00000000000..729ead80e28 --- /dev/null +++ b/libraries/eos-vm @@ -0,0 +1 @@ +Subproject commit 729ead80e28e513d00a90066ea2948683486be5b From 6ddeb96bdf86e3581bb550579dc719e95926e32f Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 18 Jun 2019 13:11:06 -0400 Subject: [PATCH 31/88] fixed up integration with vm --- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 2 +- libraries/chain/webassembly/eos-vm.cpp | 6 +++--- libraries/eos-vm | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index f6d9ca51a85..72bf23931e2 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -19,7 +19,7 @@ namespace eosio { namespace vm { template constexpr auto get_value(WAlloc*, T&& val) -> std::enable_if_t && std::is_same_v>, S> { - return {(uint64_t)val.data.ui}; + return std::move(chain::name{(uint64_t)val.data.ui}); } // we can clean these up if we go with custom vms diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 6c7420ed69f..186a48914a1 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -25,9 +25,9 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); const auto& res = _instantiated_module->call(&context, "env", "apply", - (uint64_t)context.get_receiver(), - (uint64_t)context.get_action().account, - (uint64_t)context.get_action().name); + context.get_receiver().to_uint64_t(), + context.get_action().account.to_uint64_t(), + context.get_action().name.to_uint64_t()); //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); } diff --git a/libraries/eos-vm b/libraries/eos-vm index 729ead80e28..6df8280c6f3 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 729ead80e28e513d00a90066ea2948683486be5b +Subproject commit 6df8280c6f340f2d372f80acef3047cef9e63bcc From 6ac064640d1b4baf79033b18a9c2dc34350281c8 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 21 Jun 2019 10:54:37 -0400 Subject: [PATCH 32/88] update eos-vm --- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 2 +- libraries/eos-vm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 72bf23931e2..20b111be80d 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -73,7 +73,7 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { eos_vm_runtime(); std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; - void immediately_exit_currently_running_module() override { _bkend->immediate_exit(); } + void immediately_exit_currently_running_module() override { _bkend->exit({}); } private: backend* _bkend; // non owning pointer to allow for immediate exit diff --git a/libraries/eos-vm b/libraries/eos-vm index 6df8280c6f3..cc5005c44c3 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 6df8280c6f340f2d372f80acef3047cef9e63bcc +Subproject commit cc5005c44c3d07504dc983ef2a6c8ff52b002f6a From 10b5c442ab0b16f7a72bde2b8609866368a9d145 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 21 Jun 2019 11:24:12 -0400 Subject: [PATCH 33/88] add script to enable autocomplete to script directory --- scripts/complete.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 scripts/complete.sh diff --git a/scripts/complete.sh b/scripts/complete.sh new file mode 100755 index 00000000000..608433af43e --- /dev/null +++ b/scripts/complete.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +_complete() +{ + COMPREPLY=() + + local cur=${COMP_WORDS[COMP_CWORD]} + local cmd=${COMP_WORDS[0]} + + choices=$( ${cmd} -- "${COMP_WORDS[@]:1}" 2>/dev/null ) + ret=$? + + if [[ "$ret" -ne 0 ]]; then + return 0 + fi + + local DISPLAY_HELP=1 + if [ "${__COMPLETE_PREV_LINE:-}" != "$COMP_LINE" ] || + [ "${__COMPLETE_PREV_POINT:-}" != "$COMP_POINT" ]; then + __COMPLETE_PREV_LINE=$COMP_LINE + __COMPLETE_PREV_POINT=$COMP_POINT + DISPLAY_HELP= + fi + + EXPANDED_PS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 | head -n 1)" + + if [[ ! -z "${choices[@]}" ]]; then + COMPREPLY=( $( compgen -W '${choices}' -- ${cur} ) ) + + if [[ ! -z "${cur}" ]]; then + return 0 + fi + fi + + if [ -n "$DISPLAY_HELP" ]; then + ${cmd} -- "${COMP_WORDS[@]:1}" 2>&1 > /dev/null + + if [[ -z "${choices[@]}" ]]; then + echo -n "${EXPANDED_PS1}" + echo -n "${COMP_WORDS[@]}" + fi + fi + + return 0 +} + +complete -F _complete $@ From 0d4d71bd1f9113d91b4bb74490b3781ccab33a5d Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 21 Jun 2019 14:25:46 -0400 Subject: [PATCH 34/88] eos-vm: use intrinsic_log --- .../eosio/chain/webassembly/eos-vm.hpp | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 20b111be80d..89be337a5b1 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -7,6 +7,11 @@ #include //eos-vm includes +#include +namespace eosio { namespace vm { + template <> + struct registered_host_functions; +} } #include // eosio specific specializations @@ -62,6 +67,105 @@ namespace eosio { namespace vm { }} // ns eosio::vm +namespace eosio { namespace vm { + template + auto create_logging_function(std::index_sequence) { + return std::function{ [](Cls* self, WAlloc* walloc, operand_stack& os) { + size_t i = sizeof...(Is) - 1; + auto& intrinsic_log = self->control.get_intrinsic_debug_log(); + if (intrinsic_log) { + eosio::chain::digest_type::encoder enc; + enc.write(walloc->template get_base_ptr(), walloc->get_current_page() * 64 * 1024); + intrinsic_log->record_intrinsic( + eosio::chain::calc_arguments_hash( + get_value::type, Args>( + walloc, std::get::type>>( + os.get_back(i - Is)))...), + enc.result()); + } + if constexpr (!std::is_same_v) { + if constexpr (std::is_same_v) { + R res = std::invoke(F, get_value::type, Args>( + walloc, std::get::type>>( + os.get_back(i - Is)))...); + os.trim(sizeof...(Is)); + os.push(resolve_result(std::move(res), walloc)); + } else { + R res = std::invoke(F, construct_derived::value(*self), + get_value::type, Args>( + walloc, std::get::type>>( + os.get_back(i - Is)))...); + os.trim(sizeof...(Is)); + os.push(resolve_result(std::move(res), walloc)); + } + } else { + if constexpr (std::is_same_v) { + std::invoke(F, get_value::type, Args>( + walloc, std::get::type>>( + os.get_back(i - Is)))...); + } else { + std::invoke(F, construct_derived::value(*self), + get_value::type, Args>( + walloc, std::get::type>>( + os.get_back(i - Is)))...); + } + os.trim(sizeof...(Is)); + } + } }; + } + + template <> + struct registered_host_functions { + using Cls = eosio::chain::apply_context; + + template + struct mappings { + std::unordered_map, uint32_t, host_func_pair_hash> named_mapping; + std::vector host_functions; + std::vector> functions; + size_t current_index = 0; + }; + + template + static mappings& get_mappings() { + static mappings _mappings; + return _mappings; + } + + template + static void add(const std::string& mod, const std::string& name) { + using deduced_full_ts = decltype(get_args_full(Func)); + using deduced_ts = decltype(get_args(Func)); + using res_t = typename decltype(get_return_t(Func))::type; + static constexpr auto is = std::make_index_sequence::value>(); + auto& current_mappings = get_mappings(); + current_mappings.named_mapping[{ mod, name }] = current_mappings.current_index++; + current_mappings.functions.push_back(create_logging_function(is)); + } + + template + static void resolve(Module& mod) { + decltype(mod.import_functions) imports = { mod.allocator, mod.get_imported_functions_size() }; + auto& current_mappings = get_mappings(); + for (int i = 0; i < mod.imports.size(); i++) { + std::string mod_name = + std::string((char*)mod.imports[i].module_str.raw(), mod.imports[i].module_str.size()); + std::string fn_name = std::string((char*)mod.imports[i].field_str.raw(), mod.imports[i].field_str.size()); + EOS_WB_ASSERT(current_mappings.named_mapping.count({ mod_name, fn_name }), wasm_link_exception, + "no mapping for imported function"); + imports[i] = current_mappings.named_mapping[{ mod_name, fn_name }]; + } + mod.import_functions = std::move(imports); + } + + template + void operator()(Cls* host, Execution_Context& ctx, uint32_t index) { + const auto& _func = get_mappings().functions[index]; + std::invoke(_func, host, ctx.get_wasm_allocator(), ctx.get_operand_stack()); + } + }; +} } // eosio::vm + namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { using namespace fc; From 16536bd8d3753c92d2ac9626befe4ac8a21e8d0d Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 21 Jun 2019 15:50:00 -0400 Subject: [PATCH 35/88] refactor intrinsic-log-util --- programs/intrinsic-log-util/CMakeLists.txt | 6 +- programs/intrinsic-log-util/cli_parser.hpp | 4 - programs/intrinsic-log-util/diff.cpp | 14 +++ programs/intrinsic-log-util/diff.hpp | 16 ++++ programs/intrinsic-log-util/intrinsic_log.hpp | 0 programs/intrinsic-log-util/main.cpp | 94 +++++++++---------- programs/intrinsic-log-util/print.cpp | 24 +++++ programs/intrinsic-log-util/print.hpp | 33 +++++++ programs/intrinsic-log-util/root.hpp | 24 +++++ 9 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 programs/intrinsic-log-util/diff.cpp create mode 100644 programs/intrinsic-log-util/diff.hpp create mode 100644 programs/intrinsic-log-util/intrinsic_log.hpp create mode 100644 programs/intrinsic-log-util/print.cpp create mode 100644 programs/intrinsic-log-util/print.hpp create mode 100644 programs/intrinsic-log-util/root.hpp diff --git a/programs/intrinsic-log-util/CMakeLists.txt b/programs/intrinsic-log-util/CMakeLists.txt index 6850d4396df..4697c35d47a 100644 --- a/programs/intrinsic-log-util/CMakeLists.txt +++ b/programs/intrinsic-log-util/CMakeLists.txt @@ -1,4 +1,8 @@ -add_executable( intrinsic-log-util main.cpp ) +add_executable( intrinsic-log-util + main.cpp + print.cpp + diff.cpp +) if( UNIX AND NOT APPLE ) set(rt_library rt ) diff --git a/programs/intrinsic-log-util/cli_parser.hpp b/programs/intrinsic-log-util/cli_parser.hpp index 9d3f793ef76..bdb41621bed 100644 --- a/programs/intrinsic-log-util/cli_parser.hpp +++ b/programs/intrinsic-log-util/cli_parser.hpp @@ -1,7 +1,3 @@ -/** - * @file - * @copyright defined in eosio/LICENSE.txt - */ #pragma once #include diff --git a/programs/intrinsic-log-util/diff.cpp b/programs/intrinsic-log-util/diff.cpp new file mode 100644 index 00000000000..72d22ce33a3 --- /dev/null +++ b/programs/intrinsic-log-util/diff.cpp @@ -0,0 +1,14 @@ +#include "diff.hpp" + +#include +#include + +#include + +void exec_diff( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::diff& diff ) +{ + dispatcher.stream << "diff was called" << std::endl; + idump((diff.first_log.generic_string())(diff.second_log.generic_string())); +} diff --git a/programs/intrinsic-log-util/diff.hpp b/programs/intrinsic-log-util/diff.hpp new file mode 100644 index 00000000000..a8654d56b71 --- /dev/null +++ b/programs/intrinsic-log-util/diff.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "root.hpp" + +namespace configuration { + struct root; + + struct diff { + bfs::path first_log; + bfs::path second_log; + }; +} + +void exec_diff( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::diff& diff ); diff --git a/programs/intrinsic-log-util/intrinsic_log.hpp b/programs/intrinsic-log-util/intrinsic_log.hpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/programs/intrinsic-log-util/main.cpp b/programs/intrinsic-log-util/main.cpp index b8d432cdc34..e5c5550fcd0 100644 --- a/programs/intrinsic-log-util/main.cpp +++ b/programs/intrinsic-log-util/main.cpp @@ -2,17 +2,18 @@ * @file * @copyright defined in eosio/LICENSE.txt */ +#include "root.hpp" +#include "diff.hpp" +#include "print.hpp" + #include "cli_parser.hpp" -#include + #include #include -#include -#include #include -#include -#include +#include #include #include @@ -20,18 +21,11 @@ using namespace eosio; using namespace eosio::chain; -namespace bfs = boost::filesystem; namespace bpo = boost::program_options; -struct intrinsic_log { - intrinsic_log() - {} - - fc::optional log; -}; - struct print_block_subcommand - : public subcommand< + : public configuration::print_block, + public subcommand< print_block_subcommand, subcommand_style::terminal_subcommand_with_positional_arguments > @@ -70,13 +64,11 @@ struct print_block_subcommand } } } - - uint32_t block_num = 0; - std::vector trxs_filter; }; struct print_blocks_subcommand - : public subcommand< + : public configuration::print_blocks, + public subcommand< print_blocks_subcommand, subcommand_style::terminal_subcommand_without_positional_arguments > @@ -91,9 +83,9 @@ struct print_blocks_subcommand options.add_options() ("output-file,o", bpo::value(), "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") - ("first,f", bpo::value(&first_block_num)->default_value(0), + ("first,f", bpo::value(&first_block_num)->default_value(first_block_num), "the first block number to print") - ("last,l", bpo::value()->default_value(std::numeric_limits::max()), + ("last,l", bpo::value()->default_value(last_block_num), "the first block number to print") ; } @@ -104,15 +96,13 @@ struct print_blocks_subcommand FC_ASSERT( last_block_num >= first_block_num, "invalid range" ); } } - - uint32_t first_block_num = 0; - uint32_t last_block_num = 0; }; struct print_subcommand - : public subcommand< + : public configuration::print, + public subcommand< print_subcommand, - subcommand_style::invokable_and_contains_subcommands, + subcommand_style::only_contains_subcommands, print_block_subcommand, print_blocks_subcommand > @@ -125,13 +115,20 @@ struct print_subcommand void set_options( bpo::options_description& options ) { options.add_options() - ("no-pretty-print", bpo::bool_switch()->default_value(false), "avoid pretty printing") + ("log-file", bpo::value(), "path to the log file (required)") + ("no-pretty-print", bpo::bool_switch()->default_value(no_pretty_print), "avoid pretty printing") ; } + + void initialize( bpo::variables_map&& vm ) { + FC_ASSERT( vm.count( "log-file" ) > 0, "path to log file must be provided (use --log-file)" ); + log_path = bfs::canonical( vm.at( "log-file" ).as() ); + } }; struct diff_subcommand - : public subcommand< + : public configuration::diff, + public subcommand< diff_subcommand, subcommand_style::terminal_subcommand_with_positional_arguments > @@ -152,9 +149,9 @@ struct diff_subcommand bpo::positional_options_description get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc ) { options.add_options() - ("first-log", bpo::value(), + ("first-log", bpo::value(&first_log), "path to the first log file to compare") - ("second-log", bpo::value(), + ("second-log", bpo::value(&second_log), "path to the second log file to compare") ; @@ -162,15 +159,21 @@ struct diff_subcommand "first-log", "second-log" } ); } + + void initialize( bpo::variables_map&& vm ) { + first_log = bfs::canonical( first_log ); + second_log = bfs::canonical( second_log ); + } }; struct root_command - : public subcommand< - root_command, - subcommand_style::only_contains_subcommands, - print_subcommand, - diff_subcommand - > + : public configuration::root, + public subcommand< + root_command, + subcommand_style::only_contains_subcommands, + print_subcommand, + diff_subcommand + > { static constexpr const char* name = "Generic"; @@ -180,36 +183,29 @@ struct root_command void set_options( bpo::options_description& options ) { options.add_options() - ("help,h", bpo::bool_switch(&print_help)->default_value(false), "Print this help message and exit.") - ("no-detail", bpo::bool_switch()->default_value(false), "Temporarily added for testing purposes.") + ("help,h", bpo::bool_switch(&print_help)->default_value(print_help), "Print this help message and exit.") + ("no-detail", bpo::bool_switch()->default_value(no_detail), "Temporarily added for testing purposes.") ; } - - bool print_help = false; }; -class subcommand_dispatcher { +class subcommand_dispatcher : public subcommand_dispatcher_base { public: subcommand_dispatcher( std::ostream& stream ) - :stream( stream ) + :subcommand_dispatcher_base( stream ) {} void operator()( const root_command& root, const diff_subcommand& diff ) { - stream << "diff was called" << std::endl; + exec_diff( *this, root, diff ); } void operator()( const root_command& root, const print_subcommand& print, const print_blocks_subcommand& blocks ) { - stream << "print blocks was called" << std::endl; - idump((blocks.first_block_num)(blocks.last_block_num)); + exec_print_blocks( *this, root, print, blocks ); } void operator()( const root_command& root, const print_subcommand& print, const print_block_subcommand& block ) { - stream << "print block was called" << std::endl; - idump((block.block_num)(block.trxs_filter)); + exec_print_block( *this, root, print, block ); } - -protected: - std::ostream& stream; }; int main( int argc, const char** argv ) { diff --git a/programs/intrinsic-log-util/print.cpp b/programs/intrinsic-log-util/print.cpp new file mode 100644 index 00000000000..f5483bc9af0 --- /dev/null +++ b/programs/intrinsic-log-util/print.cpp @@ -0,0 +1,24 @@ +#include "print.hpp" + +#include +#include + +#include + +void exec_print_block( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::print& print, + const configuration::print_block& block ) +{ + dispatcher.stream << "print block was called" << std::endl; + idump((print.log_path.generic_string())(block.block_num)(block.trxs_filter)); +} + +void exec_print_blocks( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::print& print, + const configuration::print_blocks& blocks ) +{ + dispatcher.stream << "print blocks was called" << std::endl; + idump((print.log_path.generic_string())(blocks.first_block_num)(blocks.last_block_num)); +} diff --git a/programs/intrinsic-log-util/print.hpp b/programs/intrinsic-log-util/print.hpp new file mode 100644 index 00000000000..7eb32cab8a0 --- /dev/null +++ b/programs/intrinsic-log-util/print.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "root.hpp" + +#include + +namespace configuration { + struct print { + bfs::path log_path; + bool no_pretty_print = false; + }; + + struct print_block { + uint32_t block_num = 0; + std::vector trxs_filter; + }; + + struct print_blocks { + uint32_t first_block_num = 0; + uint32_t last_block_num = std::numeric_limits::max(); + }; + +} + +void exec_print_block( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::print& print, + const configuration::print_block& block ); + +void exec_print_blocks( subcommand_dispatcher_base& dispatcher, + const configuration::root& root, + const configuration::print& print, + const configuration::print_blocks& blocks ); diff --git a/programs/intrinsic-log-util/root.hpp b/programs/intrinsic-log-util/root.hpp new file mode 100644 index 00000000000..0f17ade8d88 --- /dev/null +++ b/programs/intrinsic-log-util/root.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace bfs = boost::filesystem; + +namespace configuration { + struct root { + bool print_help = false; + bool no_detail = false; + }; +} + +class subcommand_dispatcher_base { +public: + subcommand_dispatcher_base( std::ostream& stream ) + :stream( stream ) + {} + +public: + std::ostream& stream; +}; From f3bbba8597675560ee97c763101c6fb34342e5b0 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 21 Jun 2019 16:26:27 -0400 Subject: [PATCH 36/88] avoid getting into inconsistent state when intrinsic_debug_log::open throws --- libraries/chain/intrinsic_debug_log.cpp | 37 ++++++++++++++----------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index 9d181d96042..dc698099572 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -254,24 +254,29 @@ namespace eosio { namespace chain { uint32_t last_committed_block_num = 0; // 0 indicates no committed blocks in log - log.seekg( 0, std::ios::end ); - pos_t file_size = log.tellg(); - if( file_size > 0 ) { - pos_t last_committed_block_begin_pos{}; - FC_ASSERT( file_size > sizeof(last_committed_block_begin_pos), - "corrupted log: file size is too small to be valid" ); - log.seekg( -sizeof(last_committed_block_begin_pos), std::ios::end ); - fc::raw::unpack( log, last_committed_block_begin_pos ); - FC_ASSERT( last_committed_block_begin_pos < file_size, "corrupted log: invalid block position" ); - - log.seekg( last_committed_block_begin_pos, std::ios::beg ); - uint8_t tag = 0; - fc::raw::unpack( log, tag ); - FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); + try { + log.seekg( 0, std::ios::end ); + pos_t file_size = log.tellg(); + if( file_size > 0 ) { + pos_t last_committed_block_begin_pos{}; + FC_ASSERT( file_size > sizeof(last_committed_block_begin_pos), + "corrupted log: file size is too small to be valid" ); + log.seekg( -sizeof(last_committed_block_begin_pos), std::ios::end ); + fc::raw::unpack( log, last_committed_block_begin_pos ); + FC_ASSERT( last_committed_block_begin_pos < file_size, "corrupted log: invalid block position" ); + + log.seekg( last_committed_block_begin_pos, std::ios::beg ); + uint8_t tag = 0; + fc::raw::unpack( log, tag ); + FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - fc::raw::unpack( log, last_committed_block_num ); + fc::raw::unpack( log, last_committed_block_num ); - log_metadata.emplace( log_metadata_t{ last_committed_block_begin_pos, file_size } ); + log_metadata.emplace( log_metadata_t{ last_committed_block_begin_pos, file_size } ); + } + } catch( ... ) { + log.close(); + throw; } if( open_as_read_only ) { From f3099e012db8412cc06e676f7d3874e4b4f2875a Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 21 Jun 2019 17:12:44 -0400 Subject: [PATCH 37/88] add mode to intrinsic_debug_log to automatically call finish_block (if appropriate) on close --- .../eosio/chain/intrinsic_debug_log.hpp | 4 +- libraries/chain/intrinsic_debug_log.cpp | 31 +++++++- unittests/intrinsic_debug_log_tests.cpp | 79 +++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp index 778dd5c433b..b04166ef2f2 100644 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp @@ -29,7 +29,9 @@ namespace eosio { namespace chain { enum class open_mode { read_only, create_new, - continue_existing + continue_existing, + create_new_and_auto_finish_block, + continue_existing_and_auto_finish_block }; void open( open_mode mode = open_mode::continue_existing ); diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp index dc698099572..f2f33dd4255 100644 --- a/libraries/chain/intrinsic_debug_log.cpp +++ b/libraries/chain/intrinsic_debug_log.cpp @@ -129,8 +129,9 @@ namespace eosio { namespace chain { std::map block_data_cache; state_type state{ closed{} }; std::optional log_metadata; + bool finish_block_on_shutdown = false; - void open( bool open_as_read_only, bool start_fresh ); + void open( bool open_as_read_only, bool start_fresh, bool auto_finish_block ); void close(); void start_block( uint32_t block_num ); @@ -234,14 +235,15 @@ namespace eosio { namespace chain { ,last_committed_block_num( s.last_committed_block_num ) {} - void intrinsic_debug_log_impl::open( bool open_as_read_only, bool start_fresh ) { + void intrinsic_debug_log_impl::open( bool open_as_read_only, bool start_fresh, bool auto_finish_block ) { FC_ASSERT( !log.is_open(), "cannot open when the log is already open" ); FC_ASSERT( std::holds_alternative( state ), "state out of sync" ); std::ios_base::openmode open_mode = std::ios::in | std::ios::binary; if( open_as_read_only ) { - FC_ASSERT( !start_fresh, "start_fresh cannot be true in read-only mode" ); + FC_ASSERT( !start_fresh, "start_fresh cannot be true in read-only mode" ); + FC_ASSERT( !auto_finish_block, "auto_finish_block cannot be true in read-only mode" ); } else { open_mode |= (std::ios::out | std::ios::app); } @@ -279,6 +281,8 @@ namespace eosio { namespace chain { throw; } + finish_block_on_shutdown = auto_finish_block; + if( open_as_read_only ) { state.emplace( last_committed_block_num ); log.seekg( 0, std::ios::beg ); @@ -291,6 +295,14 @@ namespace eosio { namespace chain { void intrinsic_debug_log_impl::close() { if( !log.is_open() ) return; + if( finish_block_on_shutdown + && !std::holds_alternative( state ) + && !std::holds_alternative( state ) + && !std::holds_alternative( state ) + ) { + finish_block(); + } + std::optional truncate_pos = std::visit( overloaded{ []( closed ) -> std::optional { FC_ASSERT( false, "state out of sync" ); @@ -319,6 +331,8 @@ namespace eosio { namespace chain { log.close(); + finish_block_on_shutdown = false; + state.emplace(); log_metadata.reset(); block_data_cache.clear(); @@ -674,7 +688,16 @@ namespace eosio { namespace chain { void intrinsic_debug_log::open( open_mode mode ) { bool open_as_read_only = (mode == open_mode::read_only); - my->open( open_as_read_only, ( open_as_read_only ? false : (mode == open_mode::create_new) ) ); + bool start_fresh = false; + bool auto_finish_block = false; + + if( !open_as_read_only ) { + start_fresh = ( mode == open_mode::create_new || mode == open_mode::create_new_and_auto_finish_block ); + auto_finish_block = ( mode == open_mode::create_new_and_auto_finish_block + || mode == open_mode::continue_existing_and_auto_finish_block ); + } + + my->open( open_as_read_only, start_fresh, auto_finish_block ); } void intrinsic_debug_log::close() { diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp index 9d0261a3c02..399d1eeb7d7 100644 --- a/unittests/intrinsic_debug_log_tests.cpp +++ b/unittests/intrinsic_debug_log_tests.cpp @@ -270,4 +270,83 @@ BOOST_AUTO_TEST_CASE(equivalence_test) { BOOST_REQUIRE( !result ); } +BOOST_AUTO_TEST_CASE(auto_finish_block_test) { + fc::temp_directory tempdir; + auto ref_log1_path = tempdir.path() / "intrinsic1.log"; + auto ref_log2_path = tempdir.path() / "intrinsic2.log"; + auto log_path = tempdir.path() / "intrinsic.log"; + auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); + auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); + name alice("alice"); + name foo("foo"); + + intrinsic_debug_log ref_log1( ref_log1_path ); // reference log 1 + { + ref_log1.open(); + ref_log1.start_block( 1u ); + ref_log1.finish_block(); + ref_log1.close(); + } + + intrinsic_debug_log ref_log2( ref_log2_path ); // reference log 2 + { + ref_log2.open(); + ref_log2.start_block( 1u ); + ref_log2.finish_block(); + ref_log2.start_block( 2u ); + ref_log2.start_transaction( trx_id ); + ref_log2.start_action( 1ull, alice, alice, foo ); + ref_log2.acknowledge_intrinsic_without_recording(); + ref_log2.record_intrinsic( digest, digest ); + ref_log2.finish_block(); + ref_log2.close(); + } + + // Mode 1 (default mode): do not automatically finish any pending block. + // This mode enforces that the log remains consistent with block activity (truncate blocks if necessary). + { + { + intrinsic_debug_log log( log_path ); + log.open(); + log.start_block( 1u ); + log.finish_block(); + log.start_block( 2u ); + log.start_transaction( trx_id ); + log.start_action( 1ull, alice, alice, foo ); + log.acknowledge_intrinsic_without_recording(); + log.record_intrinsic( digest, digest ); + } + + intrinsic_debug_log log( log_path ); + log.open( intrinsic_debug_log::open_mode::read_only ); + + auto result = intrinsic_debug_log::find_first_difference( log, ref_log1 ); + BOOST_REQUIRE( !result ); + } + + // Mode 2: automatically finish any pending block. + // This mode keeps recent information in log during any shutdown by automatically finish the block even if it + // is not explicitly called. This may result in the data recorded for last block in the log to not have all + // the data that actually occurred in that block. + { + { + intrinsic_debug_log log( log_path ); + log.open( intrinsic_debug_log::open_mode::continue_existing_and_auto_finish_block ); + log.start_block( 1u ); + log.finish_block(); + log.start_block( 2u ); + log.start_transaction( trx_id); + log.start_action( 1ull, alice, alice, foo ); + log.acknowledge_intrinsic_without_recording(); + log.record_intrinsic( digest, digest ); + } + + intrinsic_debug_log log( log_path ); + log.open( intrinsic_debug_log::open_mode::read_only ); + + auto result = intrinsic_debug_log::find_first_difference( log, ref_log2 ); + BOOST_REQUIRE( !result ); + } +} + BOOST_AUTO_TEST_SUITE_END() From c479615b310a08d126cded16fe1fd7219418de62 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 25 Jun 2019 14:43:45 -0400 Subject: [PATCH 38/88] eos-vm fixes --- libraries/chain/wasm_interface.cpp | 2 +- libraries/chain/webassembly/eos-vm.cpp | 1 + libraries/eos-vm | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 95252129354..4cd22180b42 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1152,7 +1152,7 @@ class console_api : public context_aware_api { } } - void printn(const name& value) { + void printn(name value) { if ( !ignore ) { context.console_append(value.to_string()); } diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 186a48914a1..854ddd54b33 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -21,6 +21,7 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { void apply(apply_context& context) override { _instantiated_module->set_wasm_allocator( wasm_interface::get_wasm_allocator() ); + _instantiated_module->get_wasm_allocator()->reset(); //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); diff --git a/libraries/eos-vm b/libraries/eos-vm index cc5005c44c3..f1434490613 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit cc5005c44c3d07504dc983ef2a6c8ff52b002f6a +Subproject commit f14344906139cb4df2b0d9e558cb323899bdd1c3 From f7617eafc82d284ea81b56f3e5c0e2437b9652df Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 26 Jun 2019 12:00:12 -0400 Subject: [PATCH 39/88] eos-vm: fix eosio_exit --- .../chain/include/eosio/chain/webassembly/eos-vm.hpp | 12 ++++++++++-- libraries/chain/webassembly/eos-vm.cpp | 11 +++++++---- libraries/eos-vm | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 89be337a5b1..a89ee19bc68 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -177,10 +177,18 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { eos_vm_runtime(); std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; - void immediately_exit_currently_running_module() override { _bkend->exit({}); } + void immediately_exit_currently_running_module() override { + if (_bkend) + _bkend->exit({}); + } private: - backend* _bkend; // non owning pointer to allow for immediate exit + // todo: managing this will get more complicated with sync calls; + // immediately_exit_currently_running_module() should probably + // move from wasm_runtime_interface to wasm_instantiated_module_interface. + backend* _bkend = nullptr; // non owning pointer to allow for immediate exit + + friend class eos_vm_instantiated_module; }; } } } }// eosio::chain::webassembly::wabt_runtime diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 854ddd54b33..9d2b2201145 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -16,7 +16,8 @@ using backend_t = backend; class eos_vm_instantiated_module : public wasm_instantiated_module_interface { public: - eos_vm_instantiated_module(std::unique_ptr mod) : + eos_vm_instantiated_module(eos_vm_runtime* runtime, std::unique_ptr mod) : + _runtime(runtime), _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { @@ -24,15 +25,18 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module->get_wasm_allocator()->reset(); //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - + + _runtime->_bkend = _instantiated_module.get(); const auto& res = _instantiated_module->call(&context, "env", "apply", context.get_receiver().to_uint64_t(), context.get_action().account.to_uint64_t(), context.get_action().name.to_uint64_t()); + _runtime->_bkend = nullptr; //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); } private: + eos_vm_runtime* _runtime; std::unique_ptr _instantiated_module; }; @@ -45,8 +49,7 @@ std::unique_ptr eos_vm_runtime::instantiate_ wasm_code_ptr code((uint8_t*)code_bytes, 0); std::unique_ptr bkend = std::make_unique(code, code_size); registered_host_functions::resolve(bkend->get_module()); - _bkend = bkend.get(); - return std::make_unique(std::move(bkend)); + return std::make_unique(this, std::move(bkend)); } }}}} diff --git a/libraries/eos-vm b/libraries/eos-vm index f1434490613..8fdf8c4b80c 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit f14344906139cb4df2b0d9e558cb323899bdd1c3 +Subproject commit 8fdf8c4b80ce80f7bf0637f911f9444e968577ed From 26c31f28555d587b87544439f96088e214012271 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 26 Jun 2019 18:00:30 -0400 Subject: [PATCH 40/88] update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 8fdf8c4b80c..813d8e2c26b 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 8fdf8c4b80ce80f7bf0637f911f9444e968577ed +Subproject commit 813d8e2c26b21179e29aad08669d3c96685be86f From 3ff9547379ebbf4fbdcdb3f97b07979806eaca61 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 27 Jun 2019 15:49:08 -0400 Subject: [PATCH 41/88] update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 813d8e2c26b..6e24f3ec577 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 813d8e2c26b21179e29aad08669d3c96685be86f +Subproject commit 6e24f3ec577d19918d12ddb14520be8543f44a38 From 029b1b2129e14865c24024ef110647404f42b562 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 28 Jun 2019 11:15:24 -0400 Subject: [PATCH 42/88] update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 6e24f3ec577..779d8a1aa14 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 6e24f3ec577d19918d12ddb14520be8543f44a38 +Subproject commit 779d8a1aa14d14a4174c887086d58e4922ec3442 From 750c817a0e64f17d75eebb97dd1bd25d3865674b Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 1 Jul 2019 16:44:33 -0400 Subject: [PATCH 43/88] Temporary eos-vm signal handling --- libraries/chain/webassembly/eos-vm.cpp | 38 +++++++++++++++++++++----- libraries/eos-vm | 2 +- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 9d2b2201145..67f67d3738e 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -21,18 +21,42 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { - _instantiated_module->set_wasm_allocator( wasm_interface::get_wasm_allocator() ); - _instantiated_module->get_wasm_allocator()->reset(); + auto* alloc = wasm_interface::get_wasm_allocator(); + alloc->reset(); + _instantiated_module->set_wasm_allocator( alloc ); //if (!(const auto& res = _instantiated_module->run_start())) // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); _runtime->_bkend = _instantiated_module.get(); - const auto& res = _instantiated_module->call(&context, "env", "apply", - context.get_receiver().to_uint64_t(), - context.get_action().account.to_uint64_t(), - context.get_action().name.to_uint64_t()); + + Platform::HardwareTrapType trapType; + Platform::CallStack trapCallStack; + Uptr trapOperand; + trapType = Platform::catchHardwareTraps(trapCallStack,trapOperand, [&]{ + const auto& res = _instantiated_module->call(&context, "env", "apply", + context.get_receiver().to_uint64_t(), + context.get_action().account.to_uint64_t(), + context.get_action().name.to_uint64_t()); + //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); + }); _runtime->_bkend = nullptr; - //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); + + switch(trapType) + { + case Platform::HardwareTrapType::none: break; + case Platform::HardwareTrapType::accessViolation: + { + if (alloc->is_in_region(reinterpret_cast(trapOperand))) { + throw wasm_execution_error(); + } else { + elog("Access violation outside of memory reserved addresses"); + abort(); + } + } + case Platform::HardwareTrapType::stackOverflow: throw wasm_execution_error(); + case Platform::HardwareTrapType::intDivideByZeroOrOverflow: throw wasm_execution_error(); + default: Errors::unreachable(); + }; } private: diff --git a/libraries/eos-vm b/libraries/eos-vm index 779d8a1aa14..2e53974067a 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 779d8a1aa14d14a4174c887086d58e4922ec3442 +Subproject commit 2e53974067a6e596eedb97220d8a59bb39ba5412 From cf3c2e738f103c56b6f7b62abbe8effed87bfac7 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 3 Jul 2019 16:41:05 -0400 Subject: [PATCH 44/88] update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 2e53974067a..48424513985 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 2e53974067a6e596eedb97220d8a59bb39ba5412 +Subproject commit 484245139855ea34c75d986c9148537ea441ea76 From ac79dc1da22f8141d8a6faabf17b9d4a715909e1 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Fri, 12 Jul 2019 15:15:30 -0400 Subject: [PATCH 45/88] update to fast dispatcher --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 48424513985..4759c1d7fda 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 484245139855ea34c75d986c9148537ea441ea76 +Subproject commit 4759c1d7fda799aab67c0081c15888036659ccc2 From 52f897506d2df7e67b52023e21652c2883d02496 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Fri, 12 Jul 2019 15:28:57 -0400 Subject: [PATCH 46/88] update eos-vm reference --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 4759c1d7fda..bb73245839e 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 4759c1d7fda799aab67c0081c15888036659ccc2 +Subproject commit bb73245839ec839bf28bb7930400e0813825fffa From 92d92a9f8ef98d6eae6e2f2cccd53fa8fe7b6492 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Fri, 12 Jul 2019 16:30:55 -0400 Subject: [PATCH 47/88] remove std::get and comment out trap handling and other things --- .../include/eosio/chain/webassembly/eos-vm.hpp | 16 +++++++--------- libraries/chain/webassembly/eos-vm.cpp | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index a89ee19bc68..1bdc036a8f7 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -73,41 +73,39 @@ namespace eosio { namespace vm { return std::function{ [](Cls* self, WAlloc* walloc, operand_stack& os) { size_t i = sizeof...(Is) - 1; auto& intrinsic_log = self->control.get_intrinsic_debug_log(); + /* if (intrinsic_log) { eosio::chain::digest_type::encoder enc; enc.write(walloc->template get_base_ptr(), walloc->get_current_page() * 64 * 1024); intrinsic_log->record_intrinsic( eosio::chain::calc_arguments_hash( get_value::type, Args>( - walloc, std::get::type>>( + walloc, get_value::type>>()))...); os.get_back(i - Is)))...), enc.result()); } + */ if constexpr (!std::is_same_v) { if constexpr (std::is_same_v) { R res = std::invoke(F, get_value::type, Args>( - walloc, std::get::type>>( - os.get_back(i - Is)))...); + walloc, std::move(os.get_back(i - Is).get::type>>()))...); os.trim(sizeof...(Is)); os.push(resolve_result(std::move(res), walloc)); } else { R res = std::invoke(F, construct_derived::value(*self), get_value::type, Args>( - walloc, std::get::type>>( - os.get_back(i - Is)))...); + walloc, std::move(os.get_back(i - Is).get::type>>()))...); os.trim(sizeof...(Is)); os.push(resolve_result(std::move(res), walloc)); } } else { if constexpr (std::is_same_v) { std::invoke(F, get_value::type, Args>( - walloc, std::get::type>>( - os.get_back(i - Is)))...); + walloc, std::move(os.get_back(i - Is).get::type>>()))...); } else { std::invoke(F, construct_derived::value(*self), get_value::type, Args>( - walloc, std::get::type>>( - os.get_back(i - Is)))...); + walloc, std::move(os.get_back(i - Is).get::type>>()))...); } os.trim(sizeof...(Is)); } diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 67f67d3738e..7f1bfebe132 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -40,7 +40,7 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); }); _runtime->_bkend = nullptr; - + /* TODO clean this up switch(trapType) { case Platform::HardwareTrapType::none: break; @@ -57,6 +57,7 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { case Platform::HardwareTrapType::intDivideByZeroOrOverflow: throw wasm_execution_error(); default: Errors::unreachable(); }; + */ } private: From 092ac5f9fcdc04a3298b9f6af6b6e3e356607ca1 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Wed, 24 Jul 2019 11:49:39 -0400 Subject: [PATCH 48/88] fixes for eos-vm integration and update eos-vm ref --- libraries/chain/controller.cpp | 2 + .../eosio/chain/webassembly/eos-vm.hpp | 4 ++ libraries/chain/webassembly/eos-vm.cpp | 38 +++---------------- libraries/eos-vm | 2 +- 4 files changed, 12 insertions(+), 34 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index ed7d517e9ec..8ef04b493f8 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1850,6 +1850,8 @@ struct controller_impl { ("block", *b)("expected_receipt", receipt) ); const transaction_receipt_header& r = trx_receipts.back(); + if (!(r == static_cast(receipt))) + wdump((b)); EOS_ASSERT( r == static_cast(receipt), block_validate_exception, "receipt does not match", ("producer_receipt", receipt)("validator_receipt", trx_receipts.back()) ); diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 1bdc036a8f7..39e1c1645a7 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -7,11 +7,13 @@ #include //eos-vm includes +/* #include namespace eosio { namespace vm { template <> struct registered_host_functions; } } +*/ #include // eosio specific specializations @@ -67,6 +69,7 @@ namespace eosio { namespace vm { }} // ns eosio::vm +#if 0 namespace eosio { namespace vm { template auto create_logging_function(std::index_sequence) { @@ -163,6 +166,7 @@ namespace eosio { namespace vm { } }; } } // eosio::vm +#endif namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 7f1bfebe132..061f55fb929 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -22,42 +22,14 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { void apply(apply_context& context) override { auto* alloc = wasm_interface::get_wasm_allocator(); - alloc->reset(); _instantiated_module->set_wasm_allocator( alloc ); - //if (!(const auto& res = _instantiated_module->run_start())) - // EOS_ASSERT(false, wasm_execution_error, "eos-vm start function failure (${s})", ("s", res.to_string())); - _runtime->_bkend = _instantiated_module.get(); - - Platform::HardwareTrapType trapType; - Platform::CallStack trapCallStack; - Uptr trapOperand; - trapType = Platform::catchHardwareTraps(trapCallStack,trapOperand, [&]{ - const auto& res = _instantiated_module->call(&context, "env", "apply", - context.get_receiver().to_uint64_t(), - context.get_action().account.to_uint64_t(), - context.get_action().name.to_uint64_t()); - //EOS_ASSERT(res, wasm_execution_error, "eos-vm execution failure (${s})", ("s", res.to_string())); - }); + _runtime->_bkend->initialize(&context); + const auto& res = _runtime->_bkend->call(&context, "env", "apply", + context.get_receiver().to_uint64_t(), + context.get_action().account.to_uint64_t(), + context.get_action().name.to_uint64_t()); _runtime->_bkend = nullptr; - /* TODO clean this up - switch(trapType) - { - case Platform::HardwareTrapType::none: break; - case Platform::HardwareTrapType::accessViolation: - { - if (alloc->is_in_region(reinterpret_cast(trapOperand))) { - throw wasm_execution_error(); - } else { - elog("Access violation outside of memory reserved addresses"); - abort(); - } - } - case Platform::HardwareTrapType::stackOverflow: throw wasm_execution_error(); - case Platform::HardwareTrapType::intDivideByZeroOrOverflow: throw wasm_execution_error(); - default: Errors::unreachable(); - }; - */ } private: diff --git a/libraries/eos-vm b/libraries/eos-vm index bb73245839e..7326beb6e7b 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit bb73245839ec839bf28bb7930400e0813825fffa +Subproject commit 7326beb6e7bdf6c1fb824fdd37ec02fb5c5b0baf From 0695638b381fd3e005e55f33b7d38fea8810ffa5 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Wed, 24 Jul 2019 13:50:42 -0400 Subject: [PATCH 49/88] don't inject and update eos-vm ref --- .../eosio/chain/wasm_interface_private.hpp | 31 ++++++++++--------- libraries/eos-vm | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 225e44dd971..ef727ae6ceb 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -125,8 +125,9 @@ namespace eosio { namespace chain { }); trx_context.pause_billing_timer(); IR::Module module; + std::vector bytes = codeobject->code; try { - Serialization::MemoryInputStream stream((const U8*)codeobject->code.data(), codeobject->code.size()); + Serialization::MemoryInputStream stream((const U8*)bytes.data(), bytes.size()); WASM::serialize(stream, module); module.userSections.clear(); } catch(const Serialization::FatalSerializationException& e) { @@ -135,19 +136,21 @@ namespace eosio { namespace chain { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - wasm_injections::wasm_binary_injection injector(module); - injector.inject(); - - std::vector bytes; - try { - Serialization::ArrayOutputStream outstream; - WASM::serialize(outstream, module); - bytes = outstream.getBytes(); - } catch(const Serialization::FatalSerializationException& e) { - EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); - } catch(const IR::ValidationException& e) { - EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); - } + if(vm_type != wasm_interface::vm_type::eos_vm) { + wasm_injections::wasm_binary_injection injector(module); + injector.inject(); + + std::vector bytes; + try { + Serialization::ArrayOutputStream outstream; + WASM::serialize(outstream, module); + bytes = outstream.getBytes(); + } catch(const Serialization::FatalSerializationException& e) { + EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); + } catch(const IR::ValidationException& e) { + EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); + } + } wasm_instantiation_cache.modify(it, [&](auto& c) { c.module = runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module)); diff --git a/libraries/eos-vm b/libraries/eos-vm index 7326beb6e7b..e67084e17b2 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 7326beb6e7bdf6c1fb824fdd37ec02fb5c5b0baf +Subproject commit e67084e17b284570e5a13407cb8801722b2cc52a From ecc8cb55ab9220dd10fef2342215a8a25f05211b Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 25 Jul 2019 12:01:35 -0400 Subject: [PATCH 50/88] fix some issues with building, merged develop, commented out intrinsic log stuff, and updated eos-vm ref --- libraries/chain/controller.cpp | 39 +++++++++++++------ .../eosio/chain/wasm_interface_private.hpp | 5 +-- libraries/eos-vm | 2 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 8efb85c65d8..8dcb58942d0 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -214,7 +214,9 @@ struct controller_impl { chainbase::database db; chainbase::database reversible_blocks; ///< a special database to persist blocks that have successfully been applied but are still reversible block_log blog; + /* optional intrinsic_log; + */ optional pending; block_state_ptr head; fork_database fork_db; @@ -288,7 +290,7 @@ struct controller_impl { cfg.read_only ? database::read_only : database::read_write, cfg.reversible_cache_size, false, cfg.db_map_mode, cfg.db_hugepage_paths ), blog( cfg.blocks_dir ), - intrinsic_log( cfg.intrinsic_debug_log_path ? *cfg.intrinsic_debug_log_path : optional() ), + /*intrinsic_log( cfg.intrinsic_debug_log_path ? *cfg.intrinsic_debug_log_path : optional() ),*/ fork_db( cfg.state_dir ), wasmif( cfg.wasm_runtime, db ), resource_limits( db ), @@ -305,10 +307,11 @@ struct controller_impl { const vector& new_features ) { check_protocol_features( timestamp, cur_features, new_features ); } ); - + /* if( intrinsic_log ) { intrinsic_log->open(); } + */ set_activation_handler(); set_activation_handler(); @@ -613,7 +616,7 @@ struct controller_impl { while( db.revision() > head->block_num ) { db.undo(); } - + /* if( intrinsic_log && intrinsic_log->last_committed_block_num() > 0 && head->block_num != intrinsic_log->last_committed_block_num() ) { @@ -624,6 +627,7 @@ struct controller_impl { fc::remove( intrinsic_log->get_path() ); intrinsic_log->open(); } + */ protocol_features.init( db ); @@ -693,11 +697,10 @@ struct controller_impl { if( last_block_num > head->block_num ) { replay( shutdown ); // replay any irreversible and reversible blocks ahead of current head } - + /* if( intrinsic_log ) { intrinsic_log->close(); - /* // Print intrinsic_log in JSON form intrinsic_log->open( intrinsic_debug_log::open_mode::read_only ); const auto end_itr = intrinsic_log->end_block(); @@ -705,10 +708,10 @@ struct controller_impl { dlog( "\n${json}", ("json", fc::json::to_pretty_string(*itr)) ); } intrinsic_log->close(); - */ intrinsic_log.reset(); } + */ if( shutdown() ) return; @@ -1150,9 +1153,11 @@ struct controller_impl { transaction_trace_ptr trace; if( gtrx.expiration < self.pending_block_time() ) { + /* if( intrinsic_log ) { intrinsic_log->start_transaction( gtrx.trx_id ); } + */ trace = std::make_shared(); trace->id = gtrx.trx_id; trace->block_num = self.head_block_num() + 1; @@ -1173,7 +1178,7 @@ struct controller_impl { in_trx_requiring_checks = true; uint32_t cpu_time_to_bill_us = billed_cpu_time_us; - + /* auto abort_transaction_in_intrinsic_log_on_exit = fc::make_scoped_exit( [abort_transaction=static_cast(intrinsic_log), this] { if( abort_transaction ) { @@ -1184,6 +1189,7 @@ struct controller_impl { if( intrinsic_log ) { intrinsic_log->start_transaction( gtrx.trx_id ); } + */ transaction_context trx_context( self, dtrx, gtrx.trx_id, timer ); trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource @@ -1226,7 +1232,9 @@ struct controller_impl { undo_session.squash(); restore.cancel(); + /* abort_transaction_in_intrinsic_log_on_exit.cancel(); + */ return trace; } catch( const disallowed_transaction_extensions_bad_block_exception& ) { @@ -1257,7 +1265,9 @@ struct controller_impl { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); + /* abort_transaction_in_intrinsic_log_on_exit.cancel(); + */ return trace; } trace->elapsed = fc::time_point::now() - trx_context.start; @@ -1297,7 +1307,9 @@ struct controller_impl { emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); + /* abort_transaction_in_intrinsic_log_on_exit.cancel(); + */ } else { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); @@ -1423,7 +1435,7 @@ struct controller_impl { trx_context.undo(); } else { restore.cancel(); - abort_transaction_in_intrinsic_log_on_exit.cancel(); + //abort_transaction_in_intrinsic_log_on_exit.cancel(); trx_context.squash(); } @@ -1466,10 +1478,11 @@ struct controller_impl { } else { pending.emplace( maybe_session(), *head, when, confirm_block_count, new_protocol_feature_activations ); } - + /* if( intrinsic_log ) { intrinsic_log->start_block( head->block_num + 1 ); } + */ pending->_block_status = s; pending->_producer_block_id = producer_block_id; @@ -1601,10 +1614,11 @@ struct controller_impl { EOS_ASSERT( pending->_block_stage.contains(), block_validate_exception, "already called finalize_block"); try { - + /* if( intrinsic_log ) { intrinsic_log->finish_block(); } + */ auto& pbhs = pending->get_pending_block_header_state(); @@ -2050,9 +2064,11 @@ struct controller_impl { // in case we want to adapt the intrinsic_log to working during regular operation. // In that case, we would also need to add support for popping committed blocks from the intrinsic log // and call the appropriate function from controller_impl::pop_block(). + /* if( intrinsic_log ) { intrinsic_log->abort_block(); } + */ } return applied_trxs; } @@ -2326,11 +2342,12 @@ const protocol_feature_manager& controller::get_protocol_feature_manager()const { return my->protocol_features; } - +/* optional& controller::get_intrinsic_debug_log() { return my->intrinsic_log; } +*/ controller::controller( const controller::config& cfg ) :my( new controller_impl( cfg, *this, protocol_feature_set{} ) ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index ef727ae6ceb..c397d298cc0 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -125,7 +125,7 @@ namespace eosio { namespace chain { }); trx_context.pause_billing_timer(); IR::Module module; - std::vector bytes = codeobject->code; + std::vector bytes = {(const U8*)codeobject->code.data(), (const U8*)codeobject->code.data() + codeobject->code.size()}; try { Serialization::MemoryInputStream stream((const U8*)bytes.data(), bytes.size()); WASM::serialize(stream, module); @@ -136,11 +136,10 @@ namespace eosio { namespace chain { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - if(vm_type != wasm_interface::vm_type::eos_vm) { + if(vm_type != static_cast(wasm_interface::vm_type::eos_vm)) { wasm_injections::wasm_binary_injection injector(module); injector.inject(); - std::vector bytes; try { Serialization::ArrayOutputStream outstream; WASM::serialize(outstream, module); diff --git a/libraries/eos-vm b/libraries/eos-vm index e67084e17b2..bf9ad583913 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit e67084e17b284570e5a13407cb8801722b2cc52a +Subproject commit bf9ad5839133326dd1857544f4aecdbfcf951785 From 96de94abfbac408fd7f1bdf58172b2e00b8f8f5b Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 25 Jul 2019 12:06:01 -0400 Subject: [PATCH 51/88] missed some spots --- libraries/chain/apply_context.cpp | 5 +++-- libraries/chain/include/eosio/chain/apply_context.hpp | 2 +- libraries/chain/include/eosio/chain/webassembly/wabt.hpp | 6 ++++-- libraries/chain/include/eosio/chain/webassembly/wavm.hpp | 6 ++++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index e17f85b34ef..140cf62adb1 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -34,7 +34,7 @@ apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint :control(con) ,db(con.mutable_db()) ,trx_context(trx_ctx) -,intrinsic_log(con.get_intrinsic_debug_log()) +/*,intrinsic_log(con.get_intrinsic_debug_log())*/ ,recurse_depth(depth) ,first_receiver_action_ordinal(action_ordinal) ,action_ordinal(action_ordinal) @@ -53,11 +53,12 @@ apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint void apply_context::exec_one() { auto start = fc::time_point::now(); - + /* if( intrinsic_log ) { intrinsic_log->start_action( control.get_dynamic_global_properties().global_action_sequence + 1, receiver, act->account, act->name ); } + */ action_receipt r; r.receiver = receiver; diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index d7af2f987f4..0173db45598 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -562,7 +562,7 @@ class apply_context { controller& control; chainbase::database& db; ///< database where state is stored transaction_context& trx_context; ///< transaction context in which the action is running - optional& intrinsic_log; + /*optional& intrinsic_log;*/ private: const action* act = nullptr; ///< action being applied diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 68075a29d10..79de06023da 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -658,10 +658,11 @@ struct intrinsic_function_invoker { template static Ret wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues&, int) { class_from_wasm::value(vars.ctx).checktime(); - auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); + /*auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); if( intrinsic_log ) { intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); } + */ return (class_from_wasm::value(vars.ctx).*Method)(params...); } @@ -678,10 +679,11 @@ struct intrinsic_function_invoker { template static void_type wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues& args, int offset) { class_from_wasm::value(vars.ctx).checktime(); - auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); + /*auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); if( intrinsic_log ) { intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); } + */ (class_from_wasm::value(vars.ctx).*Method)(params...); return void_type(); } diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index b057d4fd823..58cd7a15458 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -645,10 +645,11 @@ struct intrinsic_function_invoker { template static Ret wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); - auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); + /*auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); if( intrinsic_log ) { intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); } + */ return (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); } @@ -668,10 +669,11 @@ struct intrinsic_function_invoker { template static void_type wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); - auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); + /*auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); if( intrinsic_log ) { intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); } + */ (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); return void_type(); } From bcf65d9208078e05b1598dc477cf9f5783e10b32 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 25 Jul 2019 12:16:17 -0400 Subject: [PATCH 52/88] last vestiges of intrinsic logger and disable building of tools and tests for eos-vm --- libraries/CMakeLists.txt | 9 +++++---- plugins/chain_plugin/chain_plugin.cpp | 3 ++- ...g_log_tests.cpp => intrinsic_debug_log_tests.cpp.bak} | 0 3 files changed, 7 insertions(+), 5 deletions(-) rename unittests/{intrinsic_debug_log_tests.cpp => intrinsic_debug_log_tests.cpp.bak} (100%) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 116324a27fc..ad7cc32f7dc 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -16,10 +16,11 @@ set(WITH_EXCEPTIONS ON CACHE BOOL "Build with exceptions enabled" FORCE) add_subdirectory( wabt ) set(USE_EXISTING_SOFTFLOAT ON CACHE BOOL "use pre-exisiting softfloat lib") -set(ENABLE_TESTS OFF) -set(ENABLE_ADDRESS_SANITIZER OFF) -set(ENABLE_UNDEFINED_BEHAVIOR_SANITIZER OFF) -set(ENABLE_PROFILE OFF) +set(ENABLE_TOOLS OFF CACHE BOOL "Build tools") +set(ENABLE_TESTS OFF CACHE BOOL "Build tests") +set(ENABLE_ADDRESS_SANITIZER OFF CACHE BOOL "Use address sanitizer") +set(ENABLE_UNDEFINED_BEHAVIOR_SANITIZER OFF CACHE BOOL "Use UB sanitizer") +set(ENABLE_PROFILE OFF CACHE BOOL "Enable for profile builds") add_subdirectory( eos-vm ) set(ENABLE_STATIC ON) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 2e8618ef7eb..dfb13dab984 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -587,10 +587,11 @@ void chain_plugin::plugin_initialize(const variables_map& options) { else my->blocks_dir = bld; } - + /* if( options.at( "debug-log-intrinsics" ).as() ) { my->chain_config->intrinsic_debug_log_path = app().data_dir() / "intrinsics.log"; } + */ protocol_feature_set pfs; { diff --git a/unittests/intrinsic_debug_log_tests.cpp b/unittests/intrinsic_debug_log_tests.cpp.bak similarity index 100% rename from unittests/intrinsic_debug_log_tests.cpp rename to unittests/intrinsic_debug_log_tests.cpp.bak From e59dc89c484618623d67a70d1171ce79d4eb2454 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Jul 2019 17:04:39 -0400 Subject: [PATCH 53/88] remove injection for eos-vm and update eos-vm ref --- .../chain/include/eosio/chain/wasm_interface_private.hpp | 7 ++++--- libraries/chain/wasm_interface.cpp | 2 +- libraries/eos-vm | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index c397d298cc0..dfe564a284d 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -15,6 +15,7 @@ #include "Runtime/Intrinsics.h" #include "Platform/Platform.h" #include "WAST/WAST.h" + #include "IR/Validate.h" #include @@ -98,7 +99,7 @@ namespace eosio { namespace chain { } const std::unique_ptr& get_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, - const uint8_t& vm_version, transaction_context& trx_context ) + const uint8_t& vm_version, transaction_context& trx_context, bool inject ) { wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); @@ -135,8 +136,7 @@ namespace eosio { namespace chain { } catch(const IR::ValidationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - - if(vm_type != static_cast(wasm_interface::vm_type::eos_vm)) { + if(inject) { wasm_injections::wasm_binary_injection injector(module); injector.inject(); @@ -160,6 +160,7 @@ namespace eosio { namespace chain { bool is_shutting_down = false; std::unique_ptr runtime_interface; + wasm_interface::vm_type runtime; typedef boost::multi_index_container< wasm_cache_entry, diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 69a1d77344d..74098e3fc99 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -76,7 +76,7 @@ namespace eosio { namespace chain { } void wasm_interface::apply( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context ) { - my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context)->apply(context); + my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context, my->runtime != wasm_interface::vm_type::eos_vm)->apply(context); } void wasm_interface::exit() { diff --git a/libraries/eos-vm b/libraries/eos-vm index bf9ad583913..78b9bc87f79 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit bf9ad5839133326dd1857544f4aecdbfcf951785 +Subproject commit 78b9bc87f79c2088ef5a1ff61bf704b8f211a34f From d59f46aadf23401341c8228ee75d72062c35b5ab Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Mon, 29 Jul 2019 17:33:45 -0400 Subject: [PATCH 54/88] just totally disable injection for now --- .../chain/include/eosio/chain/wasm_interface_private.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index dfe564a284d..e5ccee2085d 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -136,10 +136,10 @@ namespace eosio { namespace chain { } catch(const IR::ValidationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - if(inject) { + if(false && inject) { wasm_injections::wasm_binary_injection injector(module); injector.inject(); - + std::cout << "INJECTING!!!!!!!!!!!!\n"; try { Serialization::ArrayOutputStream outstream; WASM::serialize(outstream, module); From 897e0022b36e093a87f76e25e33af2a4cb6bcf02 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 30 Jul 2019 16:26:59 -0400 Subject: [PATCH 55/88] Update for the changes to eos-vm in host-functions.hpp --- .../eosio/chain/webassembly/eos-vm.hpp | 52 +++++++------------ libraries/eos-vm | 2 +- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 39e1c1645a7..57bd22d8e41 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -18,31 +18,23 @@ namespace eosio { namespace vm { // eosio specific specializations namespace eosio { namespace vm { - template <> - struct reduce_type { - typedef uint64_t type; - }; - template - constexpr auto get_value(WAlloc*, T&& val) - -> std::enable_if_t && std::is_same_v>, S> { - return std::move(chain::name{(uint64_t)val.data.ui}); - } + template<> + struct wasm_type_converter { + static auto from_wasm(uint64_t val) { + return eosio::chain::name{val}; + } + static auto to_wasm(eosio::chain::name val) { + return val.to_uint64_t(); + } + }; - // we can clean these up if we go with custom vms - template - struct reduce_type> { - typedef uint32_t type; + template + struct wasm_type_converter> { + static auto from_wasm(T* ptr) { + return eosio::chain::array_ptr(ptr); + } }; - - template - constexpr auto get_value(WAlloc* walloc, T&& val) - -> std::enable_if_t && - std::is_same_v< eosio::chain::array_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - using ptr_ty = typename S::type; - return eosio::chain::array_ptr((ptr_ty*)((walloc->template get_base_ptr())+val.data.ui)); - } template struct construct_derived { @@ -54,18 +46,12 @@ namespace eosio { namespace vm { static auto &value(eosio::chain::apply_context& ctx) { return ctx; } }; - template <> - struct reduce_type { - typedef uint32_t type; + template<> + struct wasm_type_converter { + static auto from_wasm(char* ptr) { + return eosio::chain::null_terminated_ptr{ ptr }; + } }; - - template - constexpr auto get_value(WAlloc* walloc, T&& val) - -> std::enable_if_t && - std::is_same_v< eosio::chain::null_terminated_ptr, S> && - !std::is_lvalue_reference_v && !std::is_pointer_v, S> { - return eosio::chain::null_terminated_ptr((char*)(walloc->template get_base_ptr()+val.data.ui)); - } }} // ns eosio::vm diff --git a/libraries/eos-vm b/libraries/eos-vm index 78b9bc87f79..81131d0beee 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 78b9bc87f79c2088ef5a1ff61bf704b8f211a34f +Subproject commit 81131d0beee01784c6ec692296961a6280e34eff From 2129de695ff219d4d0e2658866bfac6d0442183b Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Wed, 31 Jul 2019 18:10:12 -0400 Subject: [PATCH 56/88] Fixes for cmake softfloat --- .../eosio/chain/wasm_interface_private.hpp | 38 +++++++++++++------ libraries/eos-vm | 2 +- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index e5ccee2085d..a1dd1410354 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -20,6 +20,8 @@ #include +#include + using namespace fc; using namespace eosio::chain::webassembly; using namespace eosio::vm; @@ -126,35 +128,49 @@ namespace eosio { namespace chain { }); trx_context.pause_billing_timer(); IR::Module module; - std::vector bytes = {(const U8*)codeobject->code.data(), (const U8*)codeobject->code.data() + codeobject->code.size()}; + std::vector bytes = { + (const U8*)codeobject->code.data(), + (const U8*)codeobject->code.data() + codeobject->code.size()}; try { - Serialization::MemoryInputStream stream((const U8*)bytes.data(), bytes.size()); + Serialization::MemoryInputStream stream((const U8*)bytes.data(), + bytes.size()); WASM::serialize(stream, module); module.userSections.clear(); - } catch(const Serialization::FatalSerializationException& e) { + } catch (const Serialization::FatalSerializationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); - } catch(const IR::ValidationException& e) { + } catch (const IR::ValidationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - if(false && inject) { + if (false && inject) { wasm_injections::wasm_binary_injection injector(module); injector.inject(); - std::cout << "INJECTING!!!!!!!!!!!!\n"; try { Serialization::ArrayOutputStream outstream; WASM::serialize(outstream, module); bytes = outstream.getBytes(); - } catch(const Serialization::FatalSerializationException& e) { - EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); - } catch(const IR::ValidationException& e) { - EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); + } catch (const Serialization::FatalSerializationException& e) { + EOS_ASSERT(false, wasm_serialization_error, + e.message.c_str()); + } catch (const IR::ValidationException& e) { + EOS_ASSERT(false, wasm_serialization_error, + e.message.c_str()); } - } + } wasm_instantiation_cache.modify(it, [&](auto& c) { c.module = runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module)); }); + std::ofstream outfile("out2.wasm"); + if (codeobject) { + outfile.write(codeobject->code.data(), codeobject->code.size()); + } + + } + std::ofstream outfile("out.wasm"); + if (codeobject) { + outfile.write(codeobject->code.data(), codeobject->code.size()); } + outfile.close(); return it->module; } diff --git a/libraries/eos-vm b/libraries/eos-vm index 78b9bc87f79..949c3f03758 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 78b9bc87f79c2088ef5a1ff61bf704b8f211a34f +Subproject commit 949c3f037589ea70bd338b61fce07c573bf5a1f7 From 930005bd7ad790371c147f1d145725d23cbf886d Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 1 Aug 2019 15:58:11 -0400 Subject: [PATCH 57/88] changes for replay failure --- libraries/chain/webassembly/eos-vm.cpp | 20 ++++++++++++++------ libraries/eos-vm | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 061f55fb929..9532e27f0bc 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -22,13 +22,21 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { void apply(apply_context& context) override { auto* alloc = wasm_interface::get_wasm_allocator(); - _instantiated_module->set_wasm_allocator( alloc ); + _instantiated_module->set_wasm_allocator(alloc); _runtime->_bkend = _instantiated_module.get(); - _runtime->_bkend->initialize(&context); - const auto& res = _runtime->_bkend->call(&context, "env", "apply", - context.get_receiver().to_uint64_t(), - context.get_action().account.to_uint64_t(), - context.get_action().name.to_uint64_t()); + _runtime->_bkend->initialize(&context); + // clamp WASM memory to maximum_linear_memory/wasm_page_size + auto& module = _runtime->_bkend->get_module(); + if (module.memories.size() && + ((module.memories.at(0).limits.maximum > wasm_constraints::maximum_linear_memory / wasm_constraints::wasm_page_size) + || !module.memories.at(0).limits.flags)) { + module.memories.at(0).limits.flags = true; + module.memories.at(0).limits.maximum = wasm_constraints::maximum_linear_memory / wasm_constraints::wasm_page_size; + } + const auto& res = _runtime->_bkend->call( + &context, "env", "apply", context.get_receiver().to_uint64_t(), + context.get_action().account.to_uint64_t(), + context.get_action().name.to_uint64_t()); _runtime->_bkend = nullptr; } diff --git a/libraries/eos-vm b/libraries/eos-vm index 949c3f03758..8ee33148fb1 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 949c3f037589ea70bd338b61fce07c573bf5a1f7 +Subproject commit 8ee33148fb17f71f93ed9f8afc99b44180e735bc From a3e4df61c0e2e348311a5cc737878b5acbf49901 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Fri, 2 Aug 2019 14:18:42 -0400 Subject: [PATCH 58/88] general cleanup and fix for develop merge --- libraries/chain/controller.cpp | 2 -- .../chain/include/eosio/chain/wasm_interface.hpp | 6 +++--- .../eosio/chain/wasm_interface_private.hpp | 15 ++------------- libraries/chain/wasm_interface.cpp | 8 ++++---- unittests/CMakeLists.txt | 1 - unittests/api_tests.cpp | 1 - 6 files changed, 9 insertions(+), 24 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index e5c96f172a1..14a71422124 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1851,8 +1851,6 @@ struct controller_impl { ("block", *b)("expected_receipt", receipt) ); const transaction_receipt_header& r = trx_receipts.back(); - if (!(r == static_cast(receipt))) - wdump((b)); EOS_ASSERT( r == static_cast(receipt), block_validate_exception, "receipt does not match", ("producer_receipt", receipt)("validator_receipt", trx_receipts.back()) ); diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index ee124f9bca4..bed4081e9e8 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -75,7 +75,7 @@ namespace eosio { namespace chain { enum class vm_type { wavm, wabt, - eos_vm + eos_vm }; wasm_interface(vm_type vm, const chainbase::database& db); @@ -87,8 +87,8 @@ namespace eosio { namespace chain { //validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints static void validate(const controller& control, const bytes& code); - //get the wasm_allocator used for the linear memory for wasm - static vm::wasm_allocator* get_wasm_allocator(); + //get the wasm_allocator used for the linear memory for wasm + static vm::wasm_allocator* get_wasm_allocator(); //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index a1dd1410354..0bd0adf449e 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -101,7 +101,7 @@ namespace eosio { namespace chain { } const std::unique_ptr& get_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, - const uint8_t& vm_version, transaction_context& trx_context, bool inject ) + const uint8_t& vm_version, transaction_context& trx_context ) { wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); @@ -141,7 +141,7 @@ namespace eosio { namespace chain { } catch (const IR::ValidationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - if (false && inject) { + if (false) { wasm_injections::wasm_binary_injection injector(module); injector.inject(); try { @@ -160,23 +160,12 @@ namespace eosio { namespace chain { wasm_instantiation_cache.modify(it, [&](auto& c) { c.module = runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module)); }); - std::ofstream outfile("out2.wasm"); - if (codeobject) { - outfile.write(codeobject->code.data(), codeobject->code.size()); - } - - } - std::ofstream outfile("out.wasm"); - if (codeobject) { - outfile.write(codeobject->code.data(), codeobject->code.size()); } - outfile.close(); return it->module; } bool is_shutting_down = false; std::unique_ptr runtime_interface; - wasm_interface::vm_type runtime; typedef boost::multi_index_container< wasm_cache_entry, diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index a205b48bf68..3a0e45dc9c2 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -76,7 +76,7 @@ namespace eosio { namespace chain { } void wasm_interface::apply( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context ) { - my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context, my->runtime != wasm_interface::vm_type::eos_vm)->apply(context); + my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context)->apply(context); } void wasm_interface::exit() { @@ -226,7 +226,7 @@ class privileged_api : public context_aware_api { return context.control.set_proposed_producers( std::move(producers) ); } - int64_t set_proposed_producers( array_ptr packed_producer_schedule, size_t datalen) { + int64_t set_proposed_producers( array_ptr packed_producer_schedule, uint32_t datalen) { datastream ds( packed_producer_schedule, datalen ); vector producers; @@ -243,7 +243,7 @@ class privileged_api : public context_aware_api { return set_proposed_producers_common(std::move(producers)); } - int64_t set_proposed_producers_ex( uint64_t packed_producer_format, array_ptr packed_producer_schedule, size_t datalen) { + int64_t set_proposed_producers_ex( uint64_t packed_producer_format, array_ptr packed_producer_schedule, uint32_t datalen) { if (packed_producer_format == 0) { return set_proposed_producers(packed_producer_schedule, datalen); } else if (packed_producer_format == 1) { @@ -257,7 +257,7 @@ class privileged_api : public context_aware_api { } } - uint32_t get_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, size_t buffer_size) { + uint32_t get_blockchain_parameters_packed( array_ptr packed_blockchain_parameters, uint32_t buffer_size) { auto& gpo = context.control.get_global_properties(); auto s = fc::raw::pack_size( gpo.configuration ); diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 296228d0e16..ed870052639 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -33,7 +33,6 @@ find_package(LLVM 4.0 REQUIRED CONFIG) link_directories(${LLVM_LIBRARY_DIR}) -add_definitions(-DNON_VALIDATING_TEST) add_subdirectory(contracts) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/contracts.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/include/contracts.hpp ESCAPE_QUOTES) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 577693ec140..4447838761f 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -116,7 +116,6 @@ FC_REFLECT( u128_action, (values) ) FC_REFLECT( cf_action, (payload)(cfd_idx) ) FC_REFLECT( dtt_action, (payer)(deferred_account)(deferred_action)(permission_name)(delay_sec) ) FC_REFLECT( invalid_access_action, (code)(val)(index)(store) ) -#define NON_VALIDATING_TEST 1 #ifdef NON_VALIDATING_TEST #define TESTER tester From f4c76bf6c74b9ecdf1ed130ecfdbfb5b675f5800 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 9 Aug 2019 16:37:27 -0400 Subject: [PATCH 59/88] New branch for eos-vm JIT. --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 8ee33148fb1..8c7c492d616 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 8ee33148fb17f71f93ed9f8afc99b44180e735bc +Subproject commit 8c7c492d616053cc7404d43eb006c39911488d73 From 14aee95e8e32d92cf5b7b94daa318ff2bf2e47f9 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 12 Aug 2019 15:11:06 -0400 Subject: [PATCH 60/88] Update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 8c7c492d616..9a9d1ac7470 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 8c7c492d616053cc7404d43eb006c39911488d73 +Subproject commit 9a9d1ac74708f633940fbca13e5f5860e7134c34 From 940c0d7c2b7a4ada558a5b7e4054e8c4873b07b3 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 13 Aug 2019 18:03:09 -0400 Subject: [PATCH 61/88] Update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 9a9d1ac7470..f9efbd67ad3 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 9a9d1ac74708f633940fbca13e5f5860e7134c34 +Subproject commit f9efbd67ad3d331e867f17515c399f4fd4d19363 From 41a1b7d28e56779ed8be3213f66111c6d07946ec Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 20 Aug 2019 15:28:21 -0400 Subject: [PATCH 62/88] Update to combined jit/interpreter. --- .../include/eosio/chain/wasm_interface.hpp | 5 +++-- .../eosio/chain/wasm_interface_private.hpp | 4 +++- .../eosio/chain/webassembly/eos-vm.hpp | 4 +++- libraries/chain/wasm_interface.cpp | 2 ++ libraries/chain/webassembly/eos-vm.cpp | 20 ++++++++++++------- libraries/eos-vm | 2 +- plugins/chain_plugin/chain_plugin.cpp | 2 +- 7 files changed, 26 insertions(+), 13 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index bed4081e9e8..2007e65573f 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -75,7 +75,8 @@ namespace eosio { namespace chain { enum class vm_type { wavm, wabt, - eos_vm + eos_vm, + eos_vm_jit }; wasm_interface(vm_type vm, const chainbase::database& db); @@ -113,4 +114,4 @@ namespace eosio{ namespace chain { std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime); }} -FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(wabt)(eos_vm) ) +FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(wabt)(eos_vm)(eos_vm_jit) ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 0bd0adf449e..5cb6640991a 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -51,7 +51,9 @@ namespace eosio { namespace chain { else if(vm == wasm_interface::vm_type::wabt) runtime_interface = std::make_unique(); else if(vm == wasm_interface::vm_type::eos_vm) - runtime_interface = std::make_unique(); + runtime_interface = std::make_unique>(); + else if(vm == wasm_interface::vm_type::eos_vm_jit) + runtime_interface = std::make_unique>(); else EOS_THROW(wasm_exception, "wasm_interface_impl fall through"); } diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 39e1c1645a7..0e0194e680a 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -174,6 +174,7 @@ using namespace fc; using namespace eosio::vm; using namespace eosio::chain::webassembly::common; +template class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { public: eos_vm_runtime(); @@ -188,8 +189,9 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { // todo: managing this will get more complicated with sync calls; // immediately_exit_currently_running_module() should probably // move from wasm_runtime_interface to wasm_instantiated_module_interface. - backend* _bkend = nullptr; // non owning pointer to allow for immediate exit + backend* _bkend = nullptr; // non owning pointer to allow for immediate exit + template friend class eos_vm_instantiated_module; }; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 3a0e45dc9c2..f38a2766c1b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -2049,6 +2049,8 @@ std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) { runtime = eosio::chain::wasm_interface::vm_type::wabt; else if (s == "eos-vm") runtime = eosio::chain::wasm_interface::vm_type::eos_vm; + else if (s == "eos-vm-jit") + runtime = eosio::chain::wasm_interface::vm_type::eos_vm_jit; else in.setstate(std::ios_base::failbit); return in; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 9532e27f0bc..d28d4f2a5f3 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -11,12 +11,12 @@ using namespace eosio::vm; namespace wasm_constraints = eosio::chain::wasm_constraints; -using backend_t = backend; - +template class eos_vm_instantiated_module : public wasm_instantiated_module_interface { + using backend_t = backend; public: - eos_vm_instantiated_module(eos_vm_runtime* runtime, std::unique_ptr mod) : + eos_vm_instantiated_module(eos_vm_runtime* runtime, std::unique_ptr mod) : _runtime(runtime), _instantiated_module(std::move(mod)) {} @@ -41,20 +41,26 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { } private: - eos_vm_runtime* _runtime; + eos_vm_runtime* _runtime; std::unique_ptr _instantiated_module; }; -eos_vm_runtime::eos_vm_runtime() {} +template +eos_vm_runtime::eos_vm_runtime() {} -std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { +template +std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { + using backend_t = backend; std::ofstream mf("temp.wasm"); mf.write((char*)code_bytes, code_size); mf.close(); wasm_code_ptr code((uint8_t*)code_bytes, 0); std::unique_ptr bkend = std::make_unique(code, code_size); registered_host_functions::resolve(bkend->get_module()); - return std::make_unique(this, std::move(bkend)); + return std::make_unique>(this, std::move(bkend)); } +template class eos_vm_runtime; +template class eos_vm_runtime; + }}}} diff --git a/libraries/eos-vm b/libraries/eos-vm index f9efbd67ad3..67ce1200547 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit f9efbd67ad3d331e867f17515c399f4fd4d19363 +Subproject commit 67ce1200547269fbd6f6211208897e06ee1980a8 diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 93b2d0d620c..f4362f111f3 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -192,7 +192,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip ("protocol-features-dir", bpo::value()->default_value("protocol_features"), "the location of the protocol_features directory (absolute path or relative to application config dir)") ("checkpoint", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") - ("wasm-runtime", bpo::value()->value_name("wavm/wabt/eos-vm"), "Override default WASM runtime") + ("wasm-runtime", bpo::value()->value_name("wavm/wabt/eos-vm/eos-vm"), "Override default WASM runtime") ("abi-serializer-max-time-ms", bpo::value()->default_value(config::default_abi_serializer_max_time_ms), "Override default maximum ABI serialization time allowed in ms") ("chain-state-db-size-mb", bpo::value()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MiB) of the chain state database") From 551601be626a5597267837a61e416d4ac8b66053 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 28 Aug 2019 13:42:42 -0400 Subject: [PATCH 63/88] Use the correct code size when instantiating a module. --- libraries/chain/webassembly/eos-vm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index d28d4f2a5f3..02219f4484d 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -54,7 +54,7 @@ std::unique_ptr eos_vm_runtime::instan std::ofstream mf("temp.wasm"); mf.write((char*)code_bytes, code_size); mf.close(); - wasm_code_ptr code((uint8_t*)code_bytes, 0); + wasm_code_ptr code((uint8_t*)code_bytes, code_size); std::unique_ptr bkend = std::make_unique(code, code_size); registered_host_functions::resolve(bkend->get_module()); return std::make_unique>(this, std::move(bkend)); From d13e7fc00a2fcef259c06c3ec7bd1ebfd22fc7f7 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 30 Aug 2019 10:55:38 -0400 Subject: [PATCH 64/88] Fixes for the unit tests. - base_tester should use eos-vm when it's specified on the command line - Add a tester option for eos-vm-jit - Translate some exceptions coming out of eos-vm - Handle checktime --- .../eosio/chain/transaction_context.hpp | 2 ++ libraries/chain/webassembly/eos-vm.cpp | 27 ++++++++++++++++--- .../testing/include/eosio/testing/tester.hpp | 2 ++ libraries/testing/tester.cpp | 4 +++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 7bbe0e66b09..c8a978838fe 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -121,7 +121,9 @@ namespace eosio { namespace chain { fc::microseconds initial_objective_duration_limit; fc::microseconds objective_duration_limit; + public: // <-- HACK for eos-vm fc::time_point _deadline = fc::time_point::maximum(); + private: int64_t deadline_exception_code = block_cpu_usage_exceeded::code_value; int64_t billing_timer_exception_code = block_cpu_usage_exceeded::code_value; fc::time_point pseudo_start; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 02219f4484d..95346ff316a 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include //eos-vm includes @@ -33,10 +34,28 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { module.memories.at(0).limits.flags = true; module.memories.at(0).limits.maximum = wasm_constraints::maximum_linear_memory / wasm_constraints::wasm_page_size; } - const auto& res = _runtime->_bkend->call( - &context, "env", "apply", context.get_receiver().to_uint64_t(), - context.get_action().account.to_uint64_t(), - context.get_action().name.to_uint64_t()); + auto fn = [&]() { + const auto& res = _runtime->_bkend->call( + &context, "env", "apply", context.get_receiver().to_uint64_t(), + context.get_action().account.to_uint64_t(), + context.get_action().name.to_uint64_t()); + }; + try { + if (context.trx_context._deadline == fc::time_point::maximum()) + fn(); + else { + watchdog wd(std::chrono::microseconds((context.trx_context._deadline - fc::time_point::now()).count())); + _runtime->_bkend->timed_run(wd, fn); + } + } catch(eosio::vm::timeout_exception&) { + // FIXME: just loop until checktime triggers, in case we got here first + while(true) context.trx_context.checktime(); + } catch(eosio::vm::wasm_memory_exception& e) { + FC_THROW_EXCEPTION(wasm_execution_error, "access violation"); + } catch(eosio::vm::exception& e) { + // FIXME: Do better translation + FC_THROW_EXCEPTION(wasm_execution_error, "something went wrong..."); + } _runtime->_bkend = nullptr; } diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 4b433b21f6f..ca021094079 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -402,6 +402,8 @@ namespace eosio { namespace testing { vcfg.wasm_runtime = chain::wasm_interface::vm_type::wabt; else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm")) vcfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm; + else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm-jit")) + vcfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm_jit; } return vcfg; } diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index e7e8ee6af57..e5672e6cc22 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -167,6 +167,10 @@ namespace eosio { namespace testing { cfg.wasm_runtime = chain::wasm_interface::vm_type::wavm; else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wabt")) cfg.wasm_runtime = chain::wasm_interface::vm_type::wabt; + else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm")) + cfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm; + else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm-jit")) + cfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm_jit; } open(nullptr); From 056966d4de6885f8995d71ae857a3b5e6b426e68 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 30 Aug 2019 11:00:06 -0400 Subject: [PATCH 65/88] Update eos-vm to assert-invalid. --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 67ce1200547..229cc21c07f 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 67ce1200547269fbd6f6211208897e06ee1980a8 +Subproject commit 229cc21c07f8024a6adf6b47d5b8327104f90042 From dcd48af34b89df139275dfe535339175aa552f76 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 3 Sep 2019 16:28:30 -0400 Subject: [PATCH 66/88] Add alignment for pointers, references, and array_ptr. --- .../eosio/chain/webassembly/eos-vm.hpp | 73 ++++++++++++++++++- libraries/eos-vm | 2 +- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 77d56104138..c211a1df229 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -19,6 +19,35 @@ namespace eosio { namespace vm { // eosio specific specializations namespace eosio { namespace vm { + template + struct aligned_array_wrapper { + static_assert(Align % alignof(T) == 0, "Must align to at least the alignment of T"); + aligned_array_wrapper(void* ptr, uint32_t size) : ptr(ptr), size(size) { + if (reinterpret_cast(ptr) % Align != 0) { + copy.reset(new std::remove_cv_t[size]); + memcpy( copy.get(), ptr, sizeof(T) * size ); + } + } + ~aligned_array_wrapper() { + if constexpr (!std::is_const_v) + if (copy) + memcpy( ptr, copy.get(), sizeof(T) * size); + } + constexpr operator T*() const { + if (copy) + return copy.get(); + else + return static_cast(ptr); + } + constexpr operator eosio::chain::array_ptr() const { + return eosio::chain::array_ptr{static_cast(*this)}; + } + + void* ptr; + std::unique_ptr[]> copy = nullptr; + std::size_t size; + }; + template<> struct wasm_type_converter { static auto from_wasm(uint64_t val) { @@ -29,10 +58,50 @@ namespace eosio { namespace vm { } }; + template + struct wasm_type_converter { + static auto from_wasm(void* val) { + return eosio::vm::aligned_ptr_wrapper{val}; + } + }; + + template + struct wasm_type_converter { + static auto from_wasm(void* val) { + return eosio::vm::aligned_ref_wrapper{val}; + } + }; + template struct wasm_type_converter> { - static auto from_wasm(T* ptr) { - return eosio::chain::array_ptr(ptr); + static auto from_wasm(void* ptr, uint32_t size) { + return aligned_array_wrapper(ptr, size); + } + }; + + template<> + struct wasm_type_converter> { + static auto from_wasm(void* ptr, uint32_t /*size*/) { + return eosio::chain::array_ptr((char*)ptr); + } + // memcpy/memmove + static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t /*size*/) { + return eosio::chain::array_ptr((char*)ptr); + } + // memset + static auto from_wasm(void* ptr, int /*val*/, uint32_t /*size*/) { + return eosio::chain::array_ptr((char*)ptr); + } + }; + + template<> + struct wasm_type_converter> { + static auto from_wasm(void* ptr, uint32_t /*size*/) { + return eosio::chain::array_ptr((char*)ptr); + } + // memcmp + static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t /*size*/) { + return eosio::chain::array_ptr((char*)ptr); } }; diff --git a/libraries/eos-vm b/libraries/eos-vm index b4dbadc2532..faaa21e0db0 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit b4dbadc253262ccb6d8af7b07c1bdfa94cf03b3b +Subproject commit faaa21e0db0b477a7a7e5d195994bd60b49432e1 From b29a602ee00cc2e8553d0e4acfe30b32f88fa7bb Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 4 Sep 2019 10:33:52 -0400 Subject: [PATCH 67/88] Add checking of pointers --- .../eosio/chain/webassembly/eos-vm.hpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index c211a1df229..2033936975d 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -23,6 +23,7 @@ namespace eosio { namespace vm { struct aligned_array_wrapper { static_assert(Align % alignof(T) == 0, "Must align to at least the alignment of T"); aligned_array_wrapper(void* ptr, uint32_t size) : ptr(ptr), size(size) { + validate_ptr(ptr, size); if (reinterpret_cast(ptr) % Align != 0) { copy.reset(new std::remove_cv_t[size]); memcpy( copy.get(), ptr, sizeof(T) * size ); @@ -81,26 +82,31 @@ namespace eosio { namespace vm { template<> struct wasm_type_converter> { - static auto from_wasm(void* ptr, uint32_t /*size*/) { + static auto from_wasm(void* ptr, uint32_t size) { + validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memcpy/memmove - static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t /*size*/) { + static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { + validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memset - static auto from_wasm(void* ptr, int /*val*/, uint32_t /*size*/) { + static auto from_wasm(void* ptr, int /*val*/, uint32_t size) { + validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } }; template<> struct wasm_type_converter> { - static auto from_wasm(void* ptr, uint32_t /*size*/) { + static auto from_wasm(void* ptr, uint32_t size) { + validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memcmp - static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t /*size*/) { + static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { + validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } }; @@ -117,8 +123,9 @@ namespace eosio { namespace vm { template<> struct wasm_type_converter { - static auto from_wasm(char* ptr) { - return eosio::chain::null_terminated_ptr{ ptr }; + static auto from_wasm(void* ptr) { + validate_c_str(ptr); + return eosio::chain::null_terminated_ptr{ static_cast(ptr) }; } }; From fa3f529dc9abbfab4e648d5dc8826ae8ebe524d8 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 4 Sep 2019 13:35:09 -0400 Subject: [PATCH 68/88] Update eos-vm. Fixes early replay failures. --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index faaa21e0db0..2be40fad06f 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit faaa21e0db0b477a7a7e5d195994bd60b49432e1 +Subproject commit 2be40fad06f3e5d22548e9dbafd39778508aa98f From 223a4b6d86ca36678f7bd86a7067d5c983e3c1ea Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 6 Sep 2019 10:58:01 -0400 Subject: [PATCH 69/88] Cleanup --- .../eosio/chain/webassembly/eos-vm.hpp | 188 +++--------------- libraries/chain/webassembly/eos-vm.cpp | 17 +- libraries/eos-vm | 2 +- 3 files changed, 45 insertions(+), 162 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 2033936975d..92a24b5a346 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -7,48 +7,11 @@ #include //eos-vm includes -/* -#include -namespace eosio { namespace vm { - template <> - struct registered_host_functions; -} } -*/ #include // eosio specific specializations namespace eosio { namespace vm { - template - struct aligned_array_wrapper { - static_assert(Align % alignof(T) == 0, "Must align to at least the alignment of T"); - aligned_array_wrapper(void* ptr, uint32_t size) : ptr(ptr), size(size) { - validate_ptr(ptr, size); - if (reinterpret_cast(ptr) % Align != 0) { - copy.reset(new std::remove_cv_t[size]); - memcpy( copy.get(), ptr, sizeof(T) * size ); - } - } - ~aligned_array_wrapper() { - if constexpr (!std::is_const_v) - if (copy) - memcpy( ptr, copy.get(), sizeof(T) * size); - } - constexpr operator T*() const { - if (copy) - return copy.get(); - else - return static_cast(ptr); - } - constexpr operator eosio::chain::array_ptr() const { - return eosio::chain::array_ptr{static_cast(*this)}; - } - - void* ptr; - std::unique_ptr[]> copy = nullptr; - std::size_t size; - }; - template<> struct wasm_type_converter { static auto from_wasm(uint64_t val) { @@ -60,52 +23,65 @@ namespace eosio { namespace vm { }; template - struct wasm_type_converter { - static auto from_wasm(void* val) { + struct wasm_type_converter : linear_memory_access { + auto from_wasm(void* val) { + validate_ptr(val, 1); return eosio::vm::aligned_ptr_wrapper{val}; } }; + template<> + struct wasm_type_converter : linear_memory_access { + void* to_wasm(char* val) { + validate_ptr(val, 1); + return val; + } + }; + template - struct wasm_type_converter { - static auto from_wasm(void* val) { - return eosio::vm::aligned_ref_wrapper{val}; + struct wasm_type_converter : linear_memory_access { + auto from_wasm(uint32_t val) { + EOS_VM_ASSERT( val != 0, wasm_memory_exception, "references cannot be created for null pointers" ); + void* ptr = get_ptr(val); + validate_ptr(ptr, 1); + return eosio::vm::aligned_ref_wrapper{ptr}; } }; template - struct wasm_type_converter> { - static auto from_wasm(void* ptr, uint32_t size) { - return aligned_array_wrapper(ptr, size); + struct wasm_type_converter> : linear_memory_access { + auto from_wasm(void* ptr, uint32_t size) { + validate_ptr(ptr, size); + return aligned_array_wrapper(ptr, size); } }; template<> - struct wasm_type_converter> { - static auto from_wasm(void* ptr, uint32_t size) { + struct wasm_type_converter> : linear_memory_access { + auto from_wasm(void* ptr, uint32_t size) { validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memcpy/memmove - static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { + auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memset - static auto from_wasm(void* ptr, int /*val*/, uint32_t size) { + auto from_wasm(void* ptr, int /*val*/, uint32_t size) { validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } }; template<> - struct wasm_type_converter> { - static auto from_wasm(void* ptr, uint32_t size) { + struct wasm_type_converter> : linear_memory_access { + auto from_wasm(void* ptr, uint32_t size) { validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } // memcmp - static auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { + auto from_wasm(void* ptr, eosio::chain::array_ptr /*src*/, uint32_t size) { validate_ptr(ptr, size); return eosio::chain::array_ptr((char*)ptr); } @@ -122,8 +98,8 @@ namespace eosio { namespace vm { }; template<> - struct wasm_type_converter { - static auto from_wasm(void* ptr) { + struct wasm_type_converter : linear_memory_access { + auto from_wasm(void* ptr) { validate_c_str(ptr); return eosio::chain::null_terminated_ptr{ static_cast(ptr) }; } @@ -131,105 +107,6 @@ namespace eosio { namespace vm { }} // ns eosio::vm -#if 0 -namespace eosio { namespace vm { - template - auto create_logging_function(std::index_sequence) { - return std::function{ [](Cls* self, WAlloc* walloc, operand_stack& os) { - size_t i = sizeof...(Is) - 1; - auto& intrinsic_log = self->control.get_intrinsic_debug_log(); - /* - if (intrinsic_log) { - eosio::chain::digest_type::encoder enc; - enc.write(walloc->template get_base_ptr(), walloc->get_current_page() * 64 * 1024); - intrinsic_log->record_intrinsic( - eosio::chain::calc_arguments_hash( - get_value::type, Args>( - walloc, get_value::type>>()))...); - os.get_back(i - Is)))...), - enc.result()); - } - */ - if constexpr (!std::is_same_v) { - if constexpr (std::is_same_v) { - R res = std::invoke(F, get_value::type, Args>( - walloc, std::move(os.get_back(i - Is).get::type>>()))...); - os.trim(sizeof...(Is)); - os.push(resolve_result(std::move(res), walloc)); - } else { - R res = std::invoke(F, construct_derived::value(*self), - get_value::type, Args>( - walloc, std::move(os.get_back(i - Is).get::type>>()))...); - os.trim(sizeof...(Is)); - os.push(resolve_result(std::move(res), walloc)); - } - } else { - if constexpr (std::is_same_v) { - std::invoke(F, get_value::type, Args>( - walloc, std::move(os.get_back(i - Is).get::type>>()))...); - } else { - std::invoke(F, construct_derived::value(*self), - get_value::type, Args>( - walloc, std::move(os.get_back(i - Is).get::type>>()))...); - } - os.trim(sizeof...(Is)); - } - } }; - } - - template <> - struct registered_host_functions { - using Cls = eosio::chain::apply_context; - - template - struct mappings { - std::unordered_map, uint32_t, host_func_pair_hash> named_mapping; - std::vector host_functions; - std::vector> functions; - size_t current_index = 0; - }; - - template - static mappings& get_mappings() { - static mappings _mappings; - return _mappings; - } - - template - static void add(const std::string& mod, const std::string& name) { - using deduced_full_ts = decltype(get_args_full(Func)); - using deduced_ts = decltype(get_args(Func)); - using res_t = typename decltype(get_return_t(Func))::type; - static constexpr auto is = std::make_index_sequence::value>(); - auto& current_mappings = get_mappings(); - current_mappings.named_mapping[{ mod, name }] = current_mappings.current_index++; - current_mappings.functions.push_back(create_logging_function(is)); - } - - template - static void resolve(Module& mod) { - decltype(mod.import_functions) imports = { mod.allocator, mod.get_imported_functions_size() }; - auto& current_mappings = get_mappings(); - for (int i = 0; i < mod.imports.size(); i++) { - std::string mod_name = - std::string((char*)mod.imports[i].module_str.raw(), mod.imports[i].module_str.size()); - std::string fn_name = std::string((char*)mod.imports[i].field_str.raw(), mod.imports[i].field_str.size()); - EOS_WB_ASSERT(current_mappings.named_mapping.count({ mod_name, fn_name }), wasm_link_exception, - "no mapping for imported function"); - imports[i] = current_mappings.named_mapping[{ mod_name, fn_name }]; - } - mod.import_functions = std::move(imports); - } - - template - void operator()(Cls* host, Execution_Context& ctx, uint32_t index) { - const auto& _func = get_mappings().functions[index]; - std::invoke(_func, host, ctx.get_wasm_allocator(), ctx.get_operand_stack()); - } - }; -} } // eosio::vm -#endif - namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime { using namespace fc; @@ -242,10 +119,7 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { eos_vm_runtime(); std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; - void immediately_exit_currently_running_module() override { - if (_bkend) - _bkend->exit({}); - } + void immediately_exit_currently_running_module() override; private: // todo: managing this will get more complicated with sync calls; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 95346ff316a..2ff7dad4ea7 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -67,16 +67,25 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { template eos_vm_runtime::eos_vm_runtime() {} +template +void eos_vm_runtime::immediately_exit_currently_running_module() { + throw wasm_exit{}; +} + template std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { using backend_t = backend; std::ofstream mf("temp.wasm"); mf.write((char*)code_bytes, code_size); mf.close(); - wasm_code_ptr code((uint8_t*)code_bytes, code_size); - std::unique_ptr bkend = std::make_unique(code, code_size); - registered_host_functions::resolve(bkend->get_module()); - return std::make_unique>(this, std::move(bkend)); + try { + wasm_code_ptr code((uint8_t*)code_bytes, code_size); + std::unique_ptr bkend = std::make_unique(code, code_size); + registered_host_functions::resolve(bkend->get_module()); + return std::make_unique>(this, std::move(bkend)); + } catch(eosio::vm::exception& e) { + FC_THROW_EXCEPTION(wasm_execution_error, "Error building eos-vm interp: ${e}", ("e", e.what())); + } } template class eos_vm_runtime; diff --git a/libraries/eos-vm b/libraries/eos-vm index 2be40fad06f..ee3746b378f 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 2be40fad06f3e5d22548e9dbafd39778508aa98f +Subproject commit ee3746b378f17af92b4aecceef53b6bbc38caa8a From 08cf2dd12f80beb241e0ec796f9cda0b1b9c49aa Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 6 Sep 2019 18:03:04 -0400 Subject: [PATCH 70/88] Test that we accept more than just 0 and 1 as the flags for limits type. --- libraries/eos-vm | 2 +- unittests/contracts/test_wasts.hpp | 11 ++++++++++- unittests/wasm_tests.cpp | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index ee3746b378f..438706c337e 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit ee3746b378f17af92b4aecceef53b6bbc38caa8a +Subproject commit 438706c337e15287a68dabced25055df6ba2d85f diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index 1f9f3facab6..0967b08b2e4 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -733,4 +733,13 @@ static const char depth_assert_wasm_float[] = R"=====( (global $$depth (mut i64) (i64.const ${MAX_DEPTH})) (global $$mcfloaty (mut f64) (f64.const 3.14)) ) -)====="; \ No newline at end of file +)====="; + +static const std::vector varuint_memory_flags{ + 0x00, 'a', 's', 'm', 0x01, 0x00, 0x00, 0x00, + 0x01, 0x07, 0x01, 0x60, 0x03, 0x7e, 0x7e, 0x7e, 0x00, // types + 0x03, 0x02, 0x01, 0x00, // functions + 0x04, 0x08, 0x01, 0x70, 0x80, 0x02, 0x80, 0x80, 0x80, 0x00, // memory with flags varuint(0x80 0x02) -> 0x2 + 0x07, 0x09, 0x01, 0x05, 'a', 'p', 'p', 'l', 'y', 0x00, 0x00, // exports + 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b // code +}; diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index cdbd56c64cb..c975f98b64f 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -1811,6 +1811,28 @@ BOOST_FIXTURE_TEST_CASE( depth_tests, TESTER ) try { } FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( varuint_memory_flags_tests, TESTER ) try { + produce_block(); + create_accounts( {N(memflags)} ); + produce_block(); + + set_code(N(memflags), varuint_memory_flags); + produce_block(); + + signed_transaction trx; + action act; + act.account = N(memflags); + act.name = N(); + act.authorization = vector{{N(memflags),config::active_name}}; + trx.actions.push_back(act); + set_transaction_headers(trx); + trx.sign(get_private_key( N(memflags), "active" ), control->get_chain_id()); + push_transaction(trx); + produce_block(); +} FC_LOG_AND_RETHROW() + + // TODO: restore net_usage_tests #if 0 BOOST_FIXTURE_TEST_CASE(net_usage_tests, tester ) try { From f8b7a6795c63d1461040ee6fe8eb2bf3dc551d2d Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 10 Sep 2019 14:51:46 -0400 Subject: [PATCH 71/88] Remove ccache from CMakeLists.txt to hopefully fix cicd build. --- CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6427d9e3737..6511e342f4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,18 +34,6 @@ else() set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") endif() -find_program(CCACHE_PROGRAM ccache) -find_program(SCCACHE_PROGRAM ccache) -if (SCCACHE_PROGRAM) - message(STATUS "EOSIO found sccache") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${SCCACHE_PROGRAM}") - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${SCCACHE_PROGRAM}") -elseif (CCACHE_PROGRAM) - message(STATUS "EOSIO found ccache") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}") -endif() - set( CLI_CLIENT_EXECUTABLE_NAME cleos ) set( NODE_EXECUTABLE_NAME nodeos ) set( KEY_STORE_EXECUTABLE_NAME keosd ) From 0487902e6022834698868522d167a3ae5bfa95e5 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 11 Sep 2019 15:22:57 -0400 Subject: [PATCH 72/88] Update eos-vm submodule --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 438706c337e..684bb3e43f4 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 438706c337e15287a68dabced25055df6ba2d85f +Subproject commit 684bb3e43f43c3ae0ef9bec506a51e893b805b85 From c779fe305170865e074e634cae24887a79029f03 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Thu, 12 Sep 2019 12:01:04 -0400 Subject: [PATCH 73/88] Use the existing checktime implementation for eos-vm. --- .../eosio/chain/transaction_context.hpp | 2 - libraries/chain/webassembly/eos-vm.cpp | 42 +++++++++++++++---- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 5205e9e19cf..8268eaaaa40 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -146,9 +146,7 @@ namespace eosio { namespace chain { fc::microseconds initial_objective_duration_limit; fc::microseconds objective_duration_limit; - public: // <-- HACK for eos-vm fc::time_point _deadline = fc::time_point::maximum(); - private: int64_t deadline_exception_code = block_cpu_usage_exceeded::code_value; int64_t billing_timer_exception_code = block_cpu_usage_exceeded::code_value; fc::time_point pseudo_start; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 2ff7dad4ea7..02a157872bf 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -12,6 +12,37 @@ using namespace eosio::vm; namespace wasm_constraints = eosio::chain::wasm_constraints; +namespace { + + struct checktime_watchdog { + checktime_watchdog(transaction_checktime_timer& timer) : _timer(timer) {} + template + struct guard { + guard(transaction_checktime_timer& timer, F&& func) + : _timer(timer), _func(static_cast(func)) { + _timer.set_expiration_callback(&callback, this); + } + ~guard() { + // FIXME: This works correctly only if the callback is invoked by a signal + // handler in the current thread. + _timer.set_expiration_callback(nullptr, nullptr); + } + static void callback(void* data) { + guard* self = static_cast(data); + self->_func(); + } + transaction_checktime_timer& _timer; + F _func; + }; + template + guard scoped_run(F&& func) { + return guard{_timer, static_cast(func)}; + } + transaction_checktime_timer& _timer; + }; + +} + template class eos_vm_instantiated_module : public wasm_instantiated_module_interface { using backend_t = backend; @@ -41,15 +72,10 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { context.get_action().name.to_uint64_t()); }; try { - if (context.trx_context._deadline == fc::time_point::maximum()) - fn(); - else { - watchdog wd(std::chrono::microseconds((context.trx_context._deadline - fc::time_point::now()).count())); - _runtime->_bkend->timed_run(wd, fn); - } + checktime_watchdog wd(context.trx_context.transaction_timer); + _runtime->_bkend->timed_run(wd, fn); } catch(eosio::vm::timeout_exception&) { - // FIXME: just loop until checktime triggers, in case we got here first - while(true) context.trx_context.checktime(); + context.trx_context.checktime(); } catch(eosio::vm::wasm_memory_exception& e) { FC_THROW_EXCEPTION(wasm_execution_error, "access violation"); } catch(eosio::vm::exception& e) { From afd3b21dc8f5ae678413461514f1c66b2be9e8fc Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Thu, 12 Sep 2019 13:47:34 -0400 Subject: [PATCH 74/88] Re-enable injection for wabt and wavm. --- .../chain/include/eosio/chain/wasm_interface_private.hpp | 4 +--- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 1 + .../include/eosio/chain/webassembly/runtime_interface.hpp | 7 ++++++- libraries/chain/include/eosio/chain/webassembly/wabt.hpp | 1 + libraries/chain/include/eosio/chain/webassembly/wavm.hpp | 1 + libraries/chain/webassembly/eos-vm.cpp | 5 +++++ libraries/chain/webassembly/wabt.cpp | 7 +++++++ libraries/chain/webassembly/wavm.cpp | 6 ++++++ 8 files changed, 28 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index cce09c4f3b4..2dbd63a7872 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -143,9 +143,7 @@ namespace eosio { namespace chain { } catch (const IR::ValidationException& e) { EOS_ASSERT(false, wasm_serialization_error, e.message.c_str()); } - if (false) { - wasm_injections::wasm_binary_injection injector(module); - injector.inject(); + if (runtime_interface->inject_module(module)) { try { Serialization::ArrayOutputStream outstream; WASM::serialize(outstream, module); diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 92a24b5a346..9c050d0b9a8 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -117,6 +117,7 @@ template class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { public: eos_vm_runtime(); + bool inject_module(IR::Module&) override; std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector) override; void immediately_exit_currently_running_module() override; diff --git a/libraries/chain/include/eosio/chain/webassembly/runtime_interface.hpp b/libraries/chain/include/eosio/chain/webassembly/runtime_interface.hpp index 2a9b8119b67..2f7ac8aad50 100644 --- a/libraries/chain/include/eosio/chain/webassembly/runtime_interface.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/runtime_interface.hpp @@ -2,6 +2,10 @@ #include #include +namespace IR { + struct Module; +} + namespace eosio { namespace chain { class apply_context; @@ -15,6 +19,7 @@ class wasm_instantiated_module_interface { class wasm_runtime_interface { public: + virtual bool inject_module(IR::Module& module) = 0; virtual std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector initial_memory) = 0; //immediately exit the currently running wasm_instantiated_module_interface. Yep, this assumes only one can possibly run at a time. @@ -23,4 +28,4 @@ class wasm_runtime_interface { virtual ~wasm_runtime_interface(); }; -}} \ No newline at end of file +}} diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 79de06023da..ecac8b5fdfb 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -50,6 +50,7 @@ struct intrinsic_registrator { class wabt_runtime : public eosio::chain::wasm_runtime_interface { public: wabt_runtime(); + bool inject_module(IR::Module&) override; std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector initial_memory) override; void immediately_exit_currently_running_module() override; diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 58cd7a15458..45a9af67d12 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -20,6 +20,7 @@ class wavm_runtime : public eosio::chain::wasm_runtime_interface { public: wavm_runtime(); ~wavm_runtime(); + bool inject_module(IR::Module&) override; std::unique_ptr instantiate_module(const char* code_bytes, size_t code_size, std::vector initial_memory) override; void immediately_exit_currently_running_module() override; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 02a157872bf..e3682695e5f 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -98,6 +98,11 @@ void eos_vm_runtime::immediately_exit_currently_running_module() { throw wasm_exit{}; } +template +bool eos_vm_runtime::inject_module(IR::Module& module) { + return false; +} + template std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { using backend_t = backend; diff --git a/libraries/chain/webassembly/wabt.cpp b/libraries/chain/webassembly/wabt.cpp index 394b1640d8d..a73617e199a 100644 --- a/libraries/chain/webassembly/wabt.cpp +++ b/libraries/chain/webassembly/wabt.cpp @@ -1,6 +1,7 @@ #include #include #include +#include //wabt includes #include @@ -73,6 +74,12 @@ class wabt_instantiated_module : public wasm_instantiated_module_interface { wabt_runtime::wabt_runtime() {} +bool wabt_runtime::inject_module(IR::Module& module) { + wasm_injections::wasm_binary_injection injector(module); + injector.inject(); + return true; +} + std::unique_ptr wabt_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector initial_memory) { std::unique_ptr env = std::make_unique(); for(auto it = intrinsic_registrator::get_map().begin() ; it != intrinsic_registrator::get_map().end(); ++it) { diff --git a/libraries/chain/webassembly/wavm.cpp b/libraries/chain/webassembly/wavm.cpp index 3133dc43e66..c0481c7cb86 100644 --- a/libraries/chain/webassembly/wavm.cpp +++ b/libraries/chain/webassembly/wavm.cpp @@ -133,6 +133,12 @@ wavm_runtime::wavm_runtime() { wavm_runtime::~wavm_runtime() { } +bool wavm_runtime::inject_module(IR::Module& module) { + wasm_injections::wasm_binary_injection injector(module); + injector.inject(); + return true; +} + std::unique_ptr wavm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector initial_memory) { std::unique_ptr module = std::make_unique(); try { From 2ede7b842bf9e6b53018718a255a58a0d07649df Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Thu, 12 Sep 2019 15:53:11 -0400 Subject: [PATCH 75/88] updated eos-vm to handle atomic issue --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 684bb3e43f4..30b173b1b01 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 684bb3e43f43c3ae0ef9bec506a51e893b805b85 +Subproject commit 30b173b1b0141621af28854638a88275b05a501f From 81a2a671073dbac888fc4e65e9607bb87d3a9037 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Thu, 12 Sep 2019 16:40:08 -0400 Subject: [PATCH 76/88] Add eos-vm to ctest and fix submodule commit. --- libraries/eos-vm | 2 +- unittests/CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 30b173b1b01..98a34860743 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 30b173b1b0141621af28854638a88275b05a501f +Subproject commit 98a34860743ff586dfefc587a5a66be4717352aa diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ed870052639..c9ff85727ce 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -62,6 +62,8 @@ foreach(TEST_SUITE ${UNIT_TESTS}) # create an independent target for each test s # to run unit_test with all log from blockchain displayed, put "--verbose" after "--", i.e. "unit_test -- --verbose" add_test(NAME ${TRIMMED_SUITE_NAME}_unit_test_wavm COMMAND unit_test --run_test=${SUITE_NAME} --report_level=detailed --color_output --catch_system_errors=no -- --wavm) add_test(NAME ${TRIMMED_SUITE_NAME}_unit_test_wabt COMMAND unit_test --run_test=${SUITE_NAME} --report_level=detailed --color_output -- --wabt) + add_test(NAME ${TRIMMED_SUITE_NAME}_unit_test_eos_vm COMMAND unit_test --run_test=${SUITE_NAME} --report_level=detailed --color_output --catch_system_errors=no -- --eos-vm) + add_test(NAME ${TRIMMED_SUITE_NAME}_unit_test_eos_vm_jit COMMAND unit_test --run_test=${SUITE_NAME} --report_level=detailed --color_output --catch_system_errors=no -- --eos-vm-jit) # build list of tests to run during coverage testing if(NOT "" STREQUAL "${ctest_tests}") set(ctest_tests "${ctest_tests}|${TRIMMED_SUITE_NAME}_unit_test_wavm|${TRIMMED_SUITE_NAME}_unit_test_wabt") From d9a380708d27eabe577eed0ba966a944c6c5a0bf Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 13 Sep 2019 11:16:24 -0400 Subject: [PATCH 77/88] Update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 98a34860743..3a25cc10506 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 98a34860743ff586dfefc587a5a66be4717352aa +Subproject commit 3a25cc10506be4a09b87066382d9cf8b5c9c77c5 From 57dde3a65fdce083c71dee63e204d1a2036eb582 Mon Sep 17 00:00:00 2001 From: Matias Romeo Date: Mon, 16 Sep 2019 00:28:34 -0300 Subject: [PATCH 78/88] Add eos-vm-jit to the list of possible runtimes --- plugins/chain_plugin/chain_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 6b529f98114..73ca06e34b9 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -188,7 +188,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip ("protocol-features-dir", bpo::value()->default_value("protocol_features"), "the location of the protocol_features directory (absolute path or relative to application config dir)") ("checkpoint", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") - ("wasm-runtime", bpo::value()->value_name("wavm/wabt/eos-vm/eos-vm"), "Override default WASM runtime") + ("wasm-runtime", bpo::value()->value_name("wavm/wabt/eos-vm/eos-vm-jit"), "Override default WASM runtime") ("abi-serializer-max-time-ms", bpo::value()->default_value(config::default_abi_serializer_max_time_ms), "Override default maximum ABI serialization time allowed in ms") ("chain-state-db-size-mb", bpo::value()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MiB) of the chain state database") From 353f417ccd421e77400e9efd4345e512f9e9908c Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 16 Sep 2019 16:14:04 -0400 Subject: [PATCH 79/88] Fix data races for eos-vm's handling of checktime - Make sure that set_expiration_callback blocks until any pending callback completes. This cannot deadlock as long as the callback doesn't touch the timer. - Make platform_timer::expired thread-safe. C++ only guarantees that sig_atomic_t is safe for signal handlers executed in the same thread as other accesses. - Invoke the callback in eos-vm.cpp even if the timer expires before we set up the callback. - std::atomic variables are only AS-safe if is_lock_free() is true. Destroying a platform_timer is still unsafe. --- libraries/chain/include/eosio/chain/platform_timer.hpp | 9 +++++---- .../chain/include/eosio/chain/transaction_context.hpp | 2 +- libraries/chain/platform_timer_posix.cpp | 2 ++ libraries/chain/webassembly/eos-vm.cpp | 5 +++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/platform_timer.hpp b/libraries/chain/include/eosio/chain/platform_timer.hpp index a5874e685cb..29a8d62d46f 100644 --- a/libraries/chain/include/eosio/chain/platform_timer.hpp +++ b/libraries/chain/include/eosio/chain/platform_timer.hpp @@ -34,7 +34,7 @@ struct platform_timer { _expiration_callback_data = user; } - volatile sig_atomic_t expired = 1; + std::atomic_bool expired = true; private: struct impl; @@ -46,9 +46,10 @@ struct platform_timer { if(atomic_compare_exchange_strong(&_callback_variables_busy, &expect_false, true)) { void(*cb)(void*) = _expiration_callback; void* cb_data = _expiration_callback_data; - _callback_variables_busy.store(false, std::memory_order_release); - if(cb) + if(cb) { cb(cb_data); + } + _callback_variables_busy.store(false, std::memory_order_release); } } @@ -57,4 +58,4 @@ struct platform_timer { void* _expiration_callback_data; }; -}} \ No newline at end of file +}} diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 8268eaaaa40..b1962fa6f5e 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -21,7 +21,7 @@ namespace eosio { namespace chain { result in an exception. Use nullptr to disable a previously set callback. */ void set_expiration_callback(void(*func)(void*), void* user); - volatile sig_atomic_t& expired; + std::atomic_bool& expired; private: platform_timer& _timer; diff --git a/libraries/chain/platform_timer_posix.cpp b/libraries/chain/platform_timer_posix.cpp index d47bd1f025a..1575664ba59 100644 --- a/libraries/chain/platform_timer_posix.cpp +++ b/libraries/chain/platform_timer_posix.cpp @@ -12,6 +12,8 @@ namespace eosio { namespace chain { +static_assert(std::atomic_bool::is_always_lock_free, "Only lock-free atomics AS-safe."); + struct platform_timer::impl { timer_t timerid; diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index e3682695e5f..0fd07b83e57 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -21,10 +21,11 @@ namespace { guard(transaction_checktime_timer& timer, F&& func) : _timer(timer), _func(static_cast(func)) { _timer.set_expiration_callback(&callback, this); + if(_timer.expired) { + _func(); // it's harmless if _func is invoked twice + } } ~guard() { - // FIXME: This works correctly only if the callback is invoked by a signal - // handler in the current thread. _timer.set_expiration_callback(nullptr, nullptr); } static void callback(void* data) { From 24931c7c050671ad45d86e3db7c4cceef6db9a54 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 20 Sep 2019 10:55:12 -0400 Subject: [PATCH 80/88] Remove all traces of intrinsic_debug_log --- libraries/chain/apply_context.cpp | 7 - libraries/chain/controller.cpp | 90 -- .../include/eosio/chain/apply_context.hpp | 7 +- .../chain/include/eosio/chain/controller.hpp | 3 - .../eosio/chain/intrinsic_debug_log.hpp | 187 ---- .../eosio/chain/webassembly/common.hpp | 21 - .../include/eosio/chain/webassembly/wabt.hpp | 12 - .../include/eosio/chain/webassembly/wavm.hpp | 12 - libraries/chain/intrinsic_debug_log.cpp | 955 ------------------ libraries/chain/webassembly/wabt.cpp | 6 - libraries/chain/webassembly/wavm.cpp | 9 - plugins/chain_plugin/chain_plugin.cpp | 7 - programs/CMakeLists.txt | 1 - programs/intrinsic-log-util/CMakeLists.txt | 30 - programs/intrinsic-log-util/cli_parser.hpp | 834 --------------- programs/intrinsic-log-util/diff.cpp | 14 - programs/intrinsic-log-util/diff.hpp | 16 - programs/intrinsic-log-util/intrinsic_log.hpp | 0 programs/intrinsic-log-util/main.cpp | 267 ----- programs/intrinsic-log-util/print.cpp | 24 - programs/intrinsic-log-util/print.hpp | 33 - programs/intrinsic-log-util/root.hpp | 24 - unittests/intrinsic_debug_log_tests.cpp.bak | 352 ------- 23 files changed, 3 insertions(+), 2908 deletions(-) delete mode 100644 libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp delete mode 100644 libraries/chain/intrinsic_debug_log.cpp delete mode 100644 programs/intrinsic-log-util/CMakeLists.txt delete mode 100644 programs/intrinsic-log-util/cli_parser.hpp delete mode 100644 programs/intrinsic-log-util/diff.cpp delete mode 100644 programs/intrinsic-log-util/diff.hpp delete mode 100644 programs/intrinsic-log-util/intrinsic_log.hpp delete mode 100644 programs/intrinsic-log-util/main.cpp delete mode 100644 programs/intrinsic-log-util/print.cpp delete mode 100644 programs/intrinsic-log-util/print.hpp delete mode 100644 programs/intrinsic-log-util/root.hpp delete mode 100644 unittests/intrinsic_debug_log_tests.cpp.bak diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index f66b3c709bb..02c964b0dee 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -34,7 +34,6 @@ apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint :control(con) ,db(con.mutable_db()) ,trx_context(trx_ctx) -/*,intrinsic_log(con.get_intrinsic_debug_log())*/ ,recurse_depth(depth) ,first_receiver_action_ordinal(action_ordinal) ,action_ordinal(action_ordinal) @@ -53,12 +52,6 @@ apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint void apply_context::exec_one() { auto start = fc::time_point::now(); - /* - if( intrinsic_log ) { - intrinsic_log->start_action( control.get_dynamic_global_properties().global_action_sequence + 1, - receiver, act->account, act->name ); - } - */ action_receipt r; r.receiver = receiver; diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 36d33788637..b11232498ba 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -217,9 +217,6 @@ struct controller_impl { chainbase::database db; chainbase::database reversible_blocks; ///< a special database to persist blocks that have successfully been applied but are still reversible block_log blog; - /* - optional intrinsic_log; - */ optional pending; block_state_ptr head; fork_database fork_db; @@ -293,7 +290,6 @@ struct controller_impl { cfg.read_only ? database::read_only : database::read_write, cfg.reversible_cache_size, false, cfg.db_map_mode, cfg.db_hugepage_paths ), blog( cfg.blocks_dir ), - /*intrinsic_log( cfg.intrinsic_debug_log_path ? *cfg.intrinsic_debug_log_path : optional() ),*/ fork_db( cfg.state_dir ), wasmif( cfg.wasm_runtime, db ), resource_limits( db ), @@ -310,11 +306,6 @@ struct controller_impl { const vector& new_features ) { check_protocol_features( timestamp, cur_features, new_features ); } ); - /* - if( intrinsic_log ) { - intrinsic_log->open(); - } - */ set_activation_handler(); set_activation_handler(); @@ -625,18 +616,6 @@ struct controller_impl { while( db.revision() > head->block_num ) { db.undo(); } - /* - if( intrinsic_log && intrinsic_log->last_committed_block_num() > 0 - && head->block_num != intrinsic_log->last_committed_block_num() ) - { - wlog( "head block num (${head}) does not match last block in intrinsic.log (${last}): replacing intrinsic.log", - ("head", head->block_num)("last", intrinsic_log->last_committed_block_num()) - ); - intrinsic_log->close(); - fc::remove( intrinsic_log->get_path() ); - intrinsic_log->open(); - } - */ protocol_features.init( db ); @@ -706,21 +685,6 @@ struct controller_impl { if( last_block_num > head->block_num ) { replay( shutdown ); // replay any irreversible and reversible blocks ahead of current head } - /* - if( intrinsic_log ) { - intrinsic_log->close(); - - // Print intrinsic_log in JSON form - intrinsic_log->open( intrinsic_debug_log::open_mode::read_only ); - const auto end_itr = intrinsic_log->end_block(); - for( auto itr = intrinsic_log->begin_block(); itr != end_itr; ++itr ) { - dlog( "\n${json}", ("json", fc::json::to_pretty_string(*itr)) ); - } - intrinsic_log->close(); - - intrinsic_log.reset(); - } - */ if( shutdown() ) return; @@ -1195,11 +1159,6 @@ struct controller_impl { transaction_trace_ptr trace; if( gtrx.expiration < self.pending_block_time() ) { - /* - if( intrinsic_log ) { - intrinsic_log->start_transaction( gtrx.trx_id ); - } - */ trace = std::make_shared(); trace->id = gtrx.trx_id; trace->block_num = self.head_block_num() + 1; @@ -1220,18 +1179,6 @@ struct controller_impl { in_trx_requiring_checks = true; uint32_t cpu_time_to_bill_us = billed_cpu_time_us; - /* - auto abort_transaction_in_intrinsic_log_on_exit = fc::make_scoped_exit( - [abort_transaction=static_cast(intrinsic_log), this] { - if( abort_transaction ) { - intrinsic_log->abort_transaction(); - } - }); - - if( intrinsic_log ) { - intrinsic_log->start_transaction( gtrx.trx_id ); - } - */ transaction_checktime_timer trx_timer(timer); transaction_context trx_context( self, dtrx, gtrx.trx_id, std::move(trx_timer) ); @@ -1275,9 +1222,6 @@ struct controller_impl { undo_session.squash(); restore.cancel(); - /* - abort_transaction_in_intrinsic_log_on_exit.cancel(); - */ return trace; } catch( const disallowed_transaction_extensions_bad_block_exception& ) { @@ -1308,9 +1252,6 @@ struct controller_impl { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); - /* - abort_transaction_in_intrinsic_log_on_exit.cancel(); - */ return trace; } trace->elapsed = fc::time_point::now() - trx_context.start; @@ -1350,9 +1291,6 @@ struct controller_impl { emit( self.applied_transaction, std::tie(trace, dtrx) ); undo_session.squash(); - /* - abort_transaction_in_intrinsic_log_on_exit.cancel(); - */ } else { emit( self.accepted_transaction, trx ); emit( self.applied_transaction, std::tie(trace, dtrx) ); @@ -1475,7 +1413,6 @@ struct controller_impl { trx_context.undo(); } else { restore.cancel(); - //abort_transaction_in_intrinsic_log_on_exit.cancel(); trx_context.squash(); } @@ -1518,11 +1455,6 @@ struct controller_impl { } else { pending.emplace( maybe_session(), *head, when, confirm_block_count, new_protocol_feature_activations ); } - /* - if( intrinsic_log ) { - intrinsic_log->start_block( head->block_num + 1 ); - } - */ pending->_block_status = s; pending->_producer_block_id = producer_block_id; @@ -1659,11 +1591,6 @@ struct controller_impl { EOS_ASSERT( pending->_block_stage.contains(), block_validate_exception, "already called finalize_block"); try { - /* - if( intrinsic_log ) { - intrinsic_log->finish_block(); - } - */ auto& pbhs = pending->get_pending_block_header_state(); @@ -2117,17 +2044,6 @@ struct controller_impl { applied_trxs = pending->extract_trx_metas(); pending.reset(); protocol_features.popped_blocks_to( head->block_num ); - - // intrinsic_log is only supported in replay at the moment and abort_block should not be called during replay. - // But this is here since intrinsic_log already supports abort_block and it sets things up for the future - // in case we want to adapt the intrinsic_log to working during regular operation. - // In that case, we would also need to add support for popping committed blocks from the intrinsic log - // and call the appropriate function from controller_impl::pop_block(). - /* - if( intrinsic_log ) { - intrinsic_log->abort_block(); - } - */ } return applied_trxs; } @@ -2401,12 +2317,6 @@ const protocol_feature_manager& controller::get_protocol_feature_manager()const { return my->protocol_features; } -/* -optional& controller::get_intrinsic_debug_log() -{ - return my->intrinsic_log; -} -*/ controller::controller( const controller::config& cfg ) :my( new controller_impl( cfg, *this, protocol_feature_set{} ) ) diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 87adbf91269..8b1cf7d6844 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -555,10 +555,9 @@ class apply_context { /// Fields: public: - controller& control; - chainbase::database& db; ///< database where state is stored - transaction_context& trx_context; ///< transaction context in which the action is running - /*optional& intrinsic_log;*/ + controller& control; + chainbase::database& db; ///< database where state is stored + transaction_context& trx_context; ///< transaction context in which the action is running private: const action* act = nullptr; ///< action being applied diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 42318bea0be..2561c30916f 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace chainbase { class database; @@ -81,7 +80,6 @@ namespace eosio { namespace chain { bool allow_ram_billing_in_notify = false; uint32_t maximum_variable_signature_length = chain::config::default_max_variable_signature_length; bool disable_all_subjective_mitigations = false; //< for testing purposes only - optional intrinsic_debug_log_path; //< for debugging purposes only genesis_state genesis; wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime; @@ -177,7 +175,6 @@ namespace eosio { namespace chain { const authorization_manager& get_authorization_manager()const; authorization_manager& get_mutable_authorization_manager(); const protocol_feature_manager& get_protocol_feature_manager()const; - optional& get_intrinsic_debug_log(); const flat_set& get_actor_whitelist() const; const flat_set& get_actor_blacklist() const; diff --git a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp b/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp deleted file mode 100644 index b04166ef2f2..00000000000 --- a/libraries/chain/include/eosio/chain/intrinsic_debug_log.hpp +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @file - * @copyright defined in eos/LICENSE - */ -#pragma once - -#include -#include - -namespace boost { - namespace filesystem { - class path; - } -} - -namespace eosio { namespace chain { - - namespace detail { - struct extended_block_data; - class intrinsic_debug_log_impl; - } - - class intrinsic_debug_log { - public: - intrinsic_debug_log( const boost::filesystem::path& log_path ); - intrinsic_debug_log( intrinsic_debug_log&& other ); - ~intrinsic_debug_log(); - - enum class open_mode { - read_only, - create_new, - continue_existing, - create_new_and_auto_finish_block, - continue_existing_and_auto_finish_block - }; - - void open( open_mode mode = open_mode::continue_existing ); - void flush(); - - /** invalidates all block_iterators */ - void close(); - - void start_block( uint32_t block_num ); - void abort_block(); - void start_transaction( const transaction_id_type& trx_id ); - void abort_transaction(); - void start_action( uint64_t global_sequence_num, - name receiver, name first_receiver, name action_name ); - void acknowledge_intrinsic_without_recording(); - void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); - void finish_block(); - - struct intrinsic_record { - uint32_t intrinsic_ordinal = 0; - digest_type arguments_hash; - digest_type memory_hash; - - friend bool operator == ( const intrinsic_record& lhs, const intrinsic_record& rhs ) { - return std::tie( lhs.intrinsic_ordinal, lhs.arguments_hash, lhs.memory_hash ) - == std::tie( rhs.intrinsic_ordinal, rhs.arguments_hash, rhs.memory_hash ); - } - - friend bool operator != ( const intrinsic_record& lhs, const intrinsic_record& rhs ) { - return !(lhs == rhs); - } - }; - - struct action_data { - uint64_t global_sequence_num = 0; - name receiver{}; - name first_receiver{}; - name action_name{}; - std::vector recorded_intrinsics; - }; - - struct transaction_data { - transaction_id_type trx_id; - std::vector< action_data > actions; - }; - - struct block_data { - uint32_t block_num = 0; - std::vector transactions; - }; - - class block_iterator : public std::iterator { - protected: - detail::intrinsic_debug_log_impl* _log = nullptr; - int64_t _pos = -1; - mutable const detail::extended_block_data* _cached_block_data = nullptr; - - protected: - explicit block_iterator( detail::intrinsic_debug_log_impl* log, int64_t pos = -1 ) - :_log(log) - ,_pos(pos) - {} - - const block_data* get_pointer()const; - - friend class intrinsic_debug_log; - - public: - block_iterator() = default; - - friend bool operator == ( const block_iterator& lhs, const block_iterator& rhs ) { - return std::tie( lhs._log, lhs._pos ) == std::tie( rhs._log, rhs._pos ); - } - - friend bool operator != ( const block_iterator& lhs, const block_iterator& rhs ) { - return !(lhs == rhs); - } - - const block_data& operator*()const { - return *get_pointer(); - } - - const block_data* operator->()const { - return get_pointer(); - } - - block_iterator& operator++(); - - block_iterator& operator--(); - - block_iterator operator++(int) { - block_iterator result(*this); - ++(*this); - return result; - } - - block_iterator operator--(int) { - block_iterator result(*this); - --(*this); - return result; - } - }; - - using block_reverse_iterator = std::reverse_iterator; - - // the four begin/end functions below can only be called in read-only mode - block_iterator begin_block()const; - block_iterator end_block()const; - block_reverse_iterator rbegin_block()const { return std::make_reverse_iterator( begin_block() ); } - block_reverse_iterator rend_blocks()const { return std::make_reverse_iterator( end_block() ); } - - bool is_open()const; - bool is_read_only()const; - - const boost::filesystem::path& get_path()const; - - /** @returns the block number of the last committed block in the log or 0 if no blocks are committed in the log */ - uint32_t last_committed_block_num()const; - - struct intrinsic_differences { - uint32_t block_num = 0; - transaction_id_type trx_id; - uint64_t global_sequence_num = 0; - name receiver{}; - name first_receiver{}; - name action_name{}; - std::vector lhs_recorded_intrinsics; - std::vector rhs_recorded_intrinsics; - }; - - /** - * @pre requires both lhs and rhs to both have the same blocks, transactions, and actions (otherwise it throws) - * @return empty optional if no difference; otherwise the intrinsic_differences for the first different action - */ - static std::optional - find_first_difference( intrinsic_debug_log& lhs, intrinsic_debug_log& rhs ); - - private: - std::unique_ptr my; - }; - -} } - -FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_record, (intrinsic_ordinal)(arguments_hash)(memory_hash) ) -FC_REFLECT( eosio::chain::intrinsic_debug_log::action_data, - (global_sequence_num)(receiver)(first_receiver)(action_name)(recorded_intrinsics) -) -FC_REFLECT( eosio::chain::intrinsic_debug_log::transaction_data, (trx_id)(actions) ) -FC_REFLECT( eosio::chain::intrinsic_debug_log::block_data, (block_num)(transactions) ) -FC_REFLECT( eosio::chain::intrinsic_debug_log::intrinsic_differences, - (block_num)(trx_id)(global_sequence_num)(receiver)(first_receiver)(action_name) - (lhs_recorded_intrinsics)(rhs_recorded_intrinsics) -) diff --git a/libraries/chain/include/eosio/chain/webassembly/common.hpp b/libraries/chain/include/eosio/chain/webassembly/common.hpp index eafcf1590b1..6ec4b9b6a2b 100644 --- a/libraries/chain/include/eosio/chain/webassembly/common.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/common.hpp @@ -74,13 +74,6 @@ namespace eosio { namespace chain { return value; } - template - friend inline DataStream& operator<<( DataStream& ds, const array_ptr& ) { - uint64_t zero = 0; - ds.write( (const char*)&zero, sizeof(zero) ); // Serializing pointers is not currently supported - return ds; - } - T *value; }; @@ -103,21 +96,7 @@ namespace eosio { namespace chain { return value; } - template - friend inline DataStream& operator<<( DataStream& ds, const null_terminated_ptr& ) { - uint64_t zero = 0; - ds.write( (const char*)&zero, sizeof(zero) ); // Serializing pointers is not currently supported - return ds; - } - char *value; }; - template - digest_type calc_arguments_hash( Params&&... params ) { - digest_type::encoder enc; - (fc::raw::pack( enc, std::forward(params) ), ...); - return enc.result(); - } - } } // eosio::chain diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index ecac8b5fdfb..8bf978272f7 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -647,8 +647,6 @@ struct intrinsic_invoker_impl> { extern apply_context* fixme_context; -digest_type calc_memory_hash( const Memory& mem ); - /** * forward declaration of a wrapper class to call methods of the class */ @@ -659,11 +657,6 @@ struct intrinsic_function_invoker { template static Ret wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues&, int) { class_from_wasm::value(vars.ctx).checktime(); - /*auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); - if( intrinsic_log ) { - intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); - } - */ return (class_from_wasm::value(vars.ctx).*Method)(params...); } @@ -680,11 +673,6 @@ struct intrinsic_function_invoker { template static void_type wrapper(wabt_apply_instance_vars& vars, Params... params, const TypedValues& args, int offset) { class_from_wasm::value(vars.ctx).checktime(); - /*auto& intrinsic_log = vars.ctx.control.get_intrinsic_debug_log(); - if( intrinsic_log ) { - intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( *vars.memory ) ); - } - */ (class_from_wasm::value(vars.ctx).*Method)(params...); return void_type(); } diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 803d41bfaab..509977e61be 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -639,8 +639,6 @@ struct intrinsic_invoker_impl, std::tuple static Ret wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); - /*auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); - if( intrinsic_log ) { - intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); - } - */ return (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); } @@ -675,11 +668,6 @@ struct intrinsic_function_invoker { template static void_type wrapper(running_instance_context& ctx, Params... params) { class_from_wasm::value(*ctx.apply_ctx).checktime(); - /*auto& intrinsic_log = ctx.apply_ctx->control.get_intrinsic_debug_log(); - if( intrinsic_log ) { - intrinsic_log->record_intrinsic( calc_arguments_hash( params... ), calc_memory_hash( ctx.memory ) ); - } - */ (class_from_wasm::value(*ctx.apply_ctx).*Method)(params...); return void_type(); } diff --git a/libraries/chain/intrinsic_debug_log.cpp b/libraries/chain/intrinsic_debug_log.cpp deleted file mode 100644 index f2f33dd4255..00000000000 --- a/libraries/chain/intrinsic_debug_log.cpp +++ /dev/null @@ -1,955 +0,0 @@ -/** - * @file - * @copyright defined in eos/LICENSE - */ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace bfs = boost::filesystem; - -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; - -using pos_t = uint64_t; - -namespace eosio { namespace chain { - - namespace detail { - - template - struct is_one_of { - static constexpr bool value = (std::is_same_v || ...); - }; - - struct extended_block_data { - extended_block_data( intrinsic_debug_log::block_data&& block_data, - const pos_t& pos, - const std::optional& previous_pos, - const std::optional& next_pos ) - :block_data( std::move(block_data) ) - ,pos( pos ) - ,previous_pos( previous_pos ) - ,next_pos( next_pos ) - {} - - intrinsic_debug_log::block_data block_data; - pos_t pos; - std::optional previous_pos; //< if empty, then this is first block in the log - std::optional next_pos; //< if empty, then this is the last block in the log - }; - - class intrinsic_debug_log_impl { - public: - static constexpr uint8_t block_tag = 0; - static constexpr uint8_t transaction_tag = 1; - static constexpr uint8_t action_tag = 2; - static constexpr uint8_t intrinsic_tag = 3; - - struct closed {}; - - struct read_only { - explicit read_only( uint32_t last_committed_block_num ) - :last_committed_block_num( last_committed_block_num ) - {} - - uint32_t last_committed_block_num; - }; - - struct in_block; - struct in_transaction; - struct in_action; - - struct waiting_to_start_block { - explicit waiting_to_start_block( uint32_t last_committed_block_num ); - explicit waiting_to_start_block( const in_block& s ); - explicit waiting_to_start_block( const in_transaction& s ); - explicit waiting_to_start_block( const in_action& s ); - - uint32_t last_committed_block_num; - }; - - struct in_block { - in_block( const waiting_to_start_block& s, pos_t pos, uint32_t num ); - explicit in_block( const in_transaction& s ); - explicit in_block( const in_action& s ); - - pos_t block_start_pos; - uint32_t block_num; - uint32_t last_committed_block_num; - bool block_header_written = false; - }; - - struct in_transaction { - in_transaction( const in_block& s, pos_t transaction_start_pos ); - in_transaction( const in_transaction& s, pos_t transaction_start_pos ); - in_transaction( const in_action& s, pos_t transaction_start_pos ); - - pos_t block_start_pos; - pos_t transaction_start_pos; - uint32_t block_num; - uint32_t last_committed_block_num; - }; - - struct in_action { - struct force_new_t {}; - - explicit in_action( const in_transaction& s ); - in_action( const in_transaction& s, force_new_t ):in_action( s ){} - in_action( const in_action& s, force_new_t ); - - pos_t block_start_pos; - pos_t transaction_start_pos; - uint32_t block_num; - uint32_t last_committed_block_num; - uint32_t intrinsic_counter = 0; - }; - - using state_type = std::variant< - closed, - read_only, - waiting_to_start_block, - in_block, - in_transaction, - in_action - >; - - struct log_metadata_t { - pos_t last_committed_block_begin_pos{}; - pos_t last_committed_block_end_pos{}; - }; - - std::fstream log; - bfs::path log_path; - std::map block_data_cache; - state_type state{ closed{} }; - std::optional log_metadata; - bool finish_block_on_shutdown = false; - - void open( bool open_as_read_only, bool start_fresh, bool auto_finish_block ); - void close(); - - void start_block( uint32_t block_num ); - void abort_block(); - void start_transaction( const transaction_id_type& trx_id ); - void abort_transaction(); - void start_action( uint64_t global_sequence_num, - name receiver, name first_receiver, name action_name ); - void acknowledge_intrinsic_without_recording(); - void record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ); - void finish_block(); - - bool is_open()const { - return !std::holds_alternative( state ); - } - - bool is_read_only()const { - return std::holds_alternative( state ); - } - - bool can_write()const { - return !std::holds_alternative( state ) && !std::holds_alternative( state ); - } - - protected: - void truncate_to( uint64_t pos ); - std::optional get_position_of_previous_block( pos_t pos ); - const extended_block_data& get_block_at_position( pos_t pos ); - - friend class intrinsic_debug_log::block_iterator; - }; - - intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( uint32_t last_committed_block_num ) - :last_committed_block_num( last_committed_block_num ) - {} - - intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_block& s ) - :last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_transaction& s ) - :last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::waiting_to_start_block::waiting_to_start_block( const in_action& s ) - :last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_block::in_block( const waiting_to_start_block& s, pos_t pos, uint32_t num ) - :block_start_pos( pos ) - ,block_num( num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_block::in_block( const in_transaction& s ) - :block_start_pos( s.block_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - ,block_header_written(true) - {} - - intrinsic_debug_log_impl::in_block::in_block( const in_action& s ) - :block_start_pos( s.block_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - ,block_header_written(true) - {} - - intrinsic_debug_log_impl::in_transaction::in_transaction( const in_block& s, pos_t transaction_start_pos ) - :block_start_pos( s.block_start_pos ) - ,transaction_start_pos( transaction_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_transaction::in_transaction( const in_transaction& s, pos_t transaction_start_pos ) - :block_start_pos( s.block_start_pos ) - ,transaction_start_pos( transaction_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_transaction::in_transaction( const in_action& s, pos_t transaction_start_pos ) - :block_start_pos( s.block_start_pos ) - ,transaction_start_pos( transaction_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_action::in_action( const in_transaction& s ) - :block_start_pos( s.block_start_pos ) - ,transaction_start_pos( s.transaction_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - intrinsic_debug_log_impl::in_action::in_action( const in_action& s, in_action::force_new_t ) - :block_start_pos( s.block_start_pos ) - ,transaction_start_pos( s.transaction_start_pos ) - ,block_num( s.block_num ) - ,last_committed_block_num( s.last_committed_block_num ) - {} - - void intrinsic_debug_log_impl::open( bool open_as_read_only, bool start_fresh, bool auto_finish_block ) { - FC_ASSERT( !log.is_open(), "cannot open when the log is already open" ); - FC_ASSERT( std::holds_alternative( state ), "state out of sync" ); - - std::ios_base::openmode open_mode = std::ios::in | std::ios::binary; - - if( open_as_read_only ) { - FC_ASSERT( !start_fresh, "start_fresh cannot be true in read-only mode" ); - FC_ASSERT( !auto_finish_block, "auto_finish_block cannot be true in read-only mode" ); - } else { - open_mode |= (std::ios::out | std::ios::app); - } - - if( start_fresh ) { - open_mode |= std::ios::trunc; - } - - log.open( log_path.generic_string().c_str(), open_mode ); - - uint32_t last_committed_block_num = 0; // 0 indicates no committed blocks in log - - try { - log.seekg( 0, std::ios::end ); - pos_t file_size = log.tellg(); - if( file_size > 0 ) { - pos_t last_committed_block_begin_pos{}; - FC_ASSERT( file_size > sizeof(last_committed_block_begin_pos), - "corrupted log: file size is too small to be valid" ); - log.seekg( -sizeof(last_committed_block_begin_pos), std::ios::end ); - fc::raw::unpack( log, last_committed_block_begin_pos ); - FC_ASSERT( last_committed_block_begin_pos < file_size, "corrupted log: invalid block position" ); - - log.seekg( last_committed_block_begin_pos, std::ios::beg ); - uint8_t tag = 0; - fc::raw::unpack( log, tag ); - FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - - fc::raw::unpack( log, last_committed_block_num ); - - log_metadata.emplace( log_metadata_t{ last_committed_block_begin_pos, file_size } ); - } - } catch( ... ) { - log.close(); - throw; - } - - finish_block_on_shutdown = auto_finish_block; - - if( open_as_read_only ) { - state.emplace( last_committed_block_num ); - log.seekg( 0, std::ios::beg ); - } else { - state.emplace( last_committed_block_num ); - log.seekp( 0, std::ios::end ); - } - } - - void intrinsic_debug_log_impl::close() { - if( !log.is_open() ) return; - - if( finish_block_on_shutdown - && !std::holds_alternative( state ) - && !std::holds_alternative( state ) - && !std::holds_alternative( state ) - ) { - finish_block(); - } - - std::optional truncate_pos = std::visit( overloaded{ - []( closed ) -> std::optional { - FC_ASSERT( false, "state out of sync" ); - return {}; // unreachable, only added for compilation purposes - }, - []( const read_only& ) -> std::optional { return {}; }, - []( const waiting_to_start_block& ) -> std::optional { return {}; }, - []( const in_block& ) -> std::optional { return {}; }, - []( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_transaction, in_action - >::value, - std::optional - > - { - return s.block_start_pos; - } - }, state ); - - if( truncate_pos ) { - truncate_to( *truncate_pos ); - } else { - log.flush(); - } - - log.close(); - - finish_block_on_shutdown = false; - - state.emplace(); - log_metadata.reset(); - block_data_cache.clear(); - } - - void intrinsic_debug_log_impl::truncate_to( pos_t pos ) { - FC_ASSERT( can_write(), "called while in invalid state" ); - - log.seekp( 0, std::ios::end ); - pos_t file_size = log.tellp(); - FC_ASSERT( 0 <= pos && pos <= file_size, "position is out of bounds" ); - - FC_ASSERT( !log_metadata || log_metadata->last_committed_block_end_pos <= pos, - "truncation would erase committed block data which is currently not supported" ); - - log.flush(); - if( pos == file_size ) return; - - if( log.tellp() > pos ) { - log.seekp( pos ); - } - - bfs::resize_file( log_path, pos ); - - log.sync(); - } - - void intrinsic_debug_log_impl::start_block( uint32_t block_num ) { - FC_ASSERT( block_num > 0, "block number cannot be 0" ); - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - [&]( const waiting_to_start_block& s ) { - FC_ASSERT( s.last_committed_block_num < block_num, - "must start a block with greater height than previously started blocks" ); - state = in_block( s, log.tellp(), block_num ); - return true; - }, - []( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_block, in_transaction, in_action - >::value, - bool - > - { - FC_ASSERT( false, "cannot start block while still processing a block" ); - return false; // unreachable, only added for compilation purposes - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - void intrinsic_debug_log_impl::abort_block() { - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - []( const waiting_to_start_block& ) { - return true; // nothing to abort - }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_block, in_transaction, in_action - >::value, - bool - > - { - truncate_to( s.block_start_pos ); - state = waiting_to_start_block( s ); - return true; - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - void intrinsic_debug_log_impl::start_transaction( const transaction_id_type& trx_id ) { - auto write_transaction_header = [&] { - uint64_t transaction_start_pos = log.tellp(); - fc::raw::pack( log, transaction_tag ); - fc::raw::pack( log, trx_id ); - return transaction_start_pos; - }; - - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - []( const waiting_to_start_block& ) { - FC_ASSERT( false, "cannot start transaction in the current state" ); - return false; // unreachable, only added for compilation purposes - }, - [&]( const in_block& s ) { - if( !s.block_header_written ) { - fc::raw::pack( log, block_tag ); - fc::raw::pack( log, s.block_num ); - } - uint64_t transaction_start_pos = write_transaction_header(); - state = in_transaction( s, transaction_start_pos ); - return true; - }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_transaction, in_action - >::value, - bool - > - { - uint64_t transaction_start_pos = write_transaction_header(); - state = in_transaction( s, transaction_start_pos ); - return true; - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - void intrinsic_debug_log_impl::abort_transaction() { - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - []( const waiting_to_start_block& ) { - FC_ASSERT( false, "cannot abort transaction in the current state" ); - return false; // unreachable, only added for compilation purposes - }, - []( const in_block& s ) { - FC_ASSERT( false, "cannot abort transaction in the current state" ); - return false; // unreachable, only added for compilation purposes - }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_transaction, in_action - >::value, - bool - > - { - truncate_to( s.transaction_start_pos ); - state = in_block( s ); - return true; - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - void intrinsic_debug_log_impl::start_action( uint64_t global_sequence_num, - name receiver, name first_receiver, name action_name ) - { - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - []( const waiting_to_start_block& ) { - FC_ASSERT( false, "cannot start action in the current state" ); - return false; // unreachable, only added for compilation purposes - }, - []( const in_block& ) { - FC_ASSERT( false, "cannot start action in the current state" ); - return false; // unreachable, only added for compilation purposes - }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_transaction, in_action - >::value, - bool - > - { - fc::raw::pack( log, action_tag ); - fc::raw::pack( log, global_sequence_num ); - fc::raw::pack( log, receiver ); - fc::raw::pack( log, first_receiver ); - fc::raw::pack( log, action_name ); - state = in_action( s, in_action::force_new_t{} ); - return true; - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - void intrinsic_debug_log_impl::acknowledge_intrinsic_without_recording() { - FC_ASSERT( can_write(), "log is not setup for writing" ); - in_action& s = std::get( state ); - ++(s.intrinsic_counter); - } - - void intrinsic_debug_log_impl::record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ) { - FC_ASSERT( can_write(), "log is not setup for writing" ); - in_action& s = std::get( state ); - - fc::raw::pack( log, intrinsic_tag ); - fc::raw::pack( log, s.intrinsic_counter ); - fc::raw::pack( log, arguments_hash ); - fc::raw::pack( log, memory_hash ); - - ++(s.intrinsic_counter); - } - - void intrinsic_debug_log_impl::finish_block() { - bool writable = std::visit( overloaded{ - []( closed ) { return false; }, - []( const read_only& ) { return false; }, - []( const waiting_to_start_block& ) { - FC_ASSERT( false, "no started block" ); - return false; // unreachable, only added for compilation purposes - }, - [&]( const in_block& s ) { - state = waiting_to_start_block( s.last_committed_block_num ); - return true; // nothing to commit - }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - in_transaction, in_action - >::value, - bool - > - { - fc::raw::pack( log, block_tag ); - fc::raw::pack( log, s.block_start_pos ); - pos_t new_last_committed_block_end_pos = log.tellp(); - log.flush(); - - pos_t new_last_committed_block_begin_pos{}; - if( log_metadata ) { - new_last_committed_block_begin_pos = log_metadata->last_committed_block_end_pos; - } - - log_metadata.emplace( log_metadata_t{ new_last_committed_block_begin_pos, new_last_committed_block_end_pos} ); - - state = waiting_to_start_block( s.last_committed_block_num ); - return true; - } - }, state ); - FC_ASSERT( writable, "log is not setup for writing" ); - } - - std::optional intrinsic_debug_log_impl::get_position_of_previous_block( pos_t pos ) { - std::optional previous_pos; - if( pos > sizeof(pos_t) ) { - pos_t read_pos = 0; - log.seekg( pos - sizeof(read_pos), std::ios::beg ); - fc::raw::unpack( log, read_pos ); - FC_ASSERT( read_pos < log.tellg(), "corrupted log: invalid block position" ); - previous_pos = read_pos; - } - return previous_pos; - } - - const extended_block_data& intrinsic_debug_log_impl::get_block_at_position( pos_t pos ) { - FC_ASSERT( is_read_only(), "only allowed in read-only mode" ); - FC_ASSERT( log_metadata && 0 <= pos && pos < log_metadata->last_committed_block_end_pos, - "position is out of bounds" ); - - auto itr = block_data_cache.find( pos ); - if( itr != block_data_cache.end() ) return itr->second; - - std::optional previous_pos = get_position_of_previous_block( pos ); - log.seekg( pos, std::ios::beg ); - - intrinsic_debug_log::block_data block_data; - - // Read all data for the current block. - { - uint8_t tag = 0; - fc::raw::unpack( log, tag ); - FC_ASSERT( tag == block_tag, "corrupted log: expected block tag" ); - fc::raw::unpack( log, block_data.block_num ); - - bool in_transaction = false; - bool in_action = false; - uint64_t minimum_accepted_global_sequence_num = 0; - - for(;;) { - fc::raw::unpack( log, tag ); - if( tag == block_tag ) { - pos_t read_pos{}; - fc::raw::unpack( log, read_pos ); - FC_ASSERT( read_pos == pos, "corrupted log: block position did not match" ); - break; - } else if( tag == transaction_tag ) { - block_data.transactions.emplace_back(); - fc::raw::unpack( log, block_data.transactions.back().trx_id ); - in_transaction = true; - in_action = false; - } else if( tag == action_tag ) { - FC_ASSERT( in_transaction, "corrupted log: no start of transaction before encountering action tag"); - uint64_t global_sequence_num = 0; - fc::raw::unpack( log, global_sequence_num ); - FC_ASSERT( global_sequence_num >= minimum_accepted_global_sequence_num, - "corrupted log: global sequence numbers of actions within a block are not monotonically increasing" - ); - block_data.transactions.back().actions.emplace_back(); - auto& act = block_data.transactions.back().actions.back(); - act.global_sequence_num = global_sequence_num; - fc::raw::unpack( log, act.receiver ); - fc::raw::unpack( log, act.first_receiver ); - fc::raw::unpack( log, act.action_name ); - in_action = true; - minimum_accepted_global_sequence_num = global_sequence_num + 1; - } else if( tag == intrinsic_tag ) { - FC_ASSERT( in_action, "corrupted log: no start of action before encountering intrinsic tag"); - auto& intrinsics = block_data.transactions.back().actions.back().recorded_intrinsics; - uint32_t minimum_accepted_ordinal = 0; - if( intrinsics.size() > 0 ) { - minimum_accepted_ordinal = intrinsics.back().intrinsic_ordinal + 1; - } - uint32_t ordinal = 0; - fc::raw::unpack( log, ordinal ); - FC_ASSERT( ordinal >= minimum_accepted_ordinal, - "corrupted log: intrinsic ordinals within an action are not monotonically increasing" ); - intrinsics.emplace_back(); - auto& intrinsic = intrinsics.back(); - intrinsic.intrinsic_ordinal = ordinal; - fc::raw::unpack( log, intrinsic.arguments_hash ); - fc::raw::unpack( log, intrinsic.memory_hash ); - } else { - FC_ASSERT( false, "corrupted log: unrecognized tag" ); - } - } - - FC_ASSERT( block_data.transactions.size() > 0, "corrupted log: empty block included" ); - } - - std::optional next_pos; - if( log.tellg() < log_metadata->last_committed_block_end_pos ) next_pos = log.tellg(); - - auto res = block_data_cache.emplace( - std::piecewise_construct, - std::forward_as_tuple( pos ), - std::forward_as_tuple( std::move(block_data), pos, previous_pos, next_pos ) - ); - return res.first->second; - } - } - - intrinsic_debug_log::intrinsic_debug_log( const bfs::path& log_path ) - :my( new detail::intrinsic_debug_log_impl() ) { - my->log_path = log_path; - my->log.exceptions( std::fstream::failbit | std::fstream::badbit ); - } - - intrinsic_debug_log::intrinsic_debug_log( intrinsic_debug_log&& other) { - my = std::move(other.my); - } - - intrinsic_debug_log::~intrinsic_debug_log() { - if( my ) { - try { - my->close(); - } FC_LOG_AND_DROP() - my.reset(); - } - } - - void intrinsic_debug_log::open( open_mode mode ) { - bool open_as_read_only = (mode == open_mode::read_only); - bool start_fresh = false; - bool auto_finish_block = false; - - if( !open_as_read_only ) { - start_fresh = ( mode == open_mode::create_new || mode == open_mode::create_new_and_auto_finish_block ); - auto_finish_block = ( mode == open_mode::create_new_and_auto_finish_block - || mode == open_mode::continue_existing_and_auto_finish_block ); - } - - my->open( open_as_read_only, start_fresh, auto_finish_block ); - } - - void intrinsic_debug_log::close() { - my->close(); - } - - void intrinsic_debug_log::flush() { - my->log.flush(); - } - - void intrinsic_debug_log::start_block( uint32_t block_num ) { - my->start_block( block_num ); - } - - void intrinsic_debug_log::abort_block() { - my->abort_block(); - } - - void intrinsic_debug_log::start_transaction( const transaction_id_type& trx_id ) { - my->start_transaction( trx_id ); - } - - void intrinsic_debug_log::abort_transaction() { - my->abort_transaction(); - } - - void intrinsic_debug_log::start_action( uint64_t global_sequence_num, - name receiver, name first_receiver, name action_name ) - { - my->start_action( global_sequence_num, receiver, first_receiver, action_name ); - } - - void intrinsic_debug_log::acknowledge_intrinsic_without_recording() { - my->acknowledge_intrinsic_without_recording(); - } - - void intrinsic_debug_log::record_intrinsic( const digest_type& arguments_hash, const digest_type& memory_hash ) { - my->record_intrinsic( arguments_hash, memory_hash ); - } - - void intrinsic_debug_log::finish_block() { - my->finish_block(); - } - - bool intrinsic_debug_log::is_open()const { - return my->is_open(); - } - - bool intrinsic_debug_log::is_read_only()const { - return my->is_read_only(); - } - - const boost::filesystem::path& intrinsic_debug_log::get_path()const { - return my->log_path; - } - - uint32_t intrinsic_debug_log::last_committed_block_num()const { - std::optional result = std::visit( overloaded{ - []( detail::intrinsic_debug_log_impl::closed ) -> std::optional { return {}; }, - [&]( auto&& s ) - -> std::enable_if_t< - detail::is_one_of< std::decay_t, - detail::intrinsic_debug_log_impl::read_only, - detail::intrinsic_debug_log_impl::waiting_to_start_block, - detail::intrinsic_debug_log_impl::in_block, - detail::intrinsic_debug_log_impl::in_transaction, - detail::intrinsic_debug_log_impl::in_action - >::value, - std::optional - > - { - return s.last_committed_block_num; - } - }, my->state ); - FC_ASSERT( result, "log is closed" ); - return *result; - } - - intrinsic_debug_log::block_iterator intrinsic_debug_log::begin_block()const { - FC_ASSERT( my->is_read_only(), "block_begin is only allowed in read-only mode" ); - return block_iterator( my.get(), (my->log_metadata ? 0ll : -1ll) ); - } - - intrinsic_debug_log::block_iterator intrinsic_debug_log::end_block()const { - FC_ASSERT( my->is_read_only(), "block_end is only allowed in read-only mode" ); - return block_iterator( my.get() ); - } - - const intrinsic_debug_log::block_data* intrinsic_debug_log::block_iterator::get_pointer()const { - FC_ASSERT( _log, "cannot dereference default constructed block iterator" ); - FC_ASSERT( _pos >= 0, "cannot dereference an end block iterator" ); - - if( !_cached_block_data ) { - _cached_block_data = &_log->get_block_at_position( _pos ); - } - - return &(_cached_block_data->block_data); - - } - - intrinsic_debug_log::block_iterator& intrinsic_debug_log::block_iterator::operator++() { - FC_ASSERT( _log, "cannot increment default constructed block iterator" ); - FC_ASSERT( _pos >= 0, "cannot increment end block iterator" ); - - if( !_cached_block_data ) { - _cached_block_data = &_log->get_block_at_position( static_cast(_pos) ); - } - - _pos = (_cached_block_data->next_pos ? static_cast(*_cached_block_data->next_pos) : -1ll); - _cached_block_data = nullptr; - - return *this; - } - - intrinsic_debug_log::block_iterator& intrinsic_debug_log::block_iterator::operator--() { - FC_ASSERT( _log, "cannot decrement default constructed block iterator" ); - - if( _pos < 0 ) { - FC_ASSERT( !_cached_block_data, "invariant failure" ); - FC_ASSERT( _log->log_metadata, "cannot decrement end block iterator when the log contains no blocks" ); - _pos = static_cast( _log->log_metadata->last_committed_block_begin_pos ); - return *this; - } - - std::optional prev_pos; - if( _cached_block_data ) { - prev_pos = _cached_block_data->previous_pos; - } else { - prev_pos = _log->get_position_of_previous_block( static_cast(_pos) ); - } - - FC_ASSERT( prev_pos, "cannot decrement begin block iterator" ); - _pos = static_cast(*prev_pos); - _cached_block_data = nullptr; - return *this; - } - - std::optional - intrinsic_debug_log::find_first_difference( intrinsic_debug_log& lhs, intrinsic_debug_log& rhs ) { - lhs.close(); - rhs.close(); - lhs.open( intrinsic_debug_log::open_mode::read_only ); - rhs.open( intrinsic_debug_log::open_mode::read_only ); - - auto close_logs = fc::make_scoped_exit([&lhs, &rhs] { - lhs.close(); - rhs.close(); - }); - - enum class mismatch_t { - equivalent, - lhs_ended_early, - rhs_ended_early, - mismatch - }; - - auto find_if_mismatched = []( auto&& lhs_begin, auto&& lhs_end, - auto&& rhs_begin, auto&& rhs_end, - auto&& compare_equivalent ) -> mismatch_t - { - for( ; lhs_begin != lhs_end && rhs_begin != rhs_end; ++lhs_begin, ++rhs_begin ) { - const auto& lhs_value = *lhs_begin; - const auto& rhs_value = *rhs_begin; - if( compare_equivalent( lhs_value, rhs_value ) ) continue; - return mismatch_t::mismatch; - } - - bool lhs_empty = (lhs_begin == lhs_end); - bool rhs_empty = (rhs_begin == rhs_end); - // lhs_empty and rhs_empty cannot both be false at this point since we exited the for loop. - - if( lhs_empty && !rhs_empty ) return mismatch_t::lhs_ended_early; - if( !lhs_empty && rhs_empty ) return mismatch_t::rhs_ended_early; - return mismatch_t::equivalent; - }; - - std::optional result; - const char* error_msg = nullptr; - - auto status = find_if_mismatched( - lhs.begin_block(), lhs.end_block(), - rhs.begin_block(), rhs.end_block(), - [&find_if_mismatched, &result, &error_msg] - ( const block_data& block_lhs, const block_data& block_rhs ) { - if( block_lhs.block_num != block_rhs.block_num ) { - error_msg = "logs do not have the same blocks"; - return false; - } - auto status2 = find_if_mismatched( - block_lhs.transactions.begin(), block_lhs.transactions.end(), - block_rhs.transactions.begin(), block_rhs.transactions.end(), - [&find_if_mismatched, &result, &error_msg, &block_lhs] - ( const transaction_data& trx_lhs, const transaction_data& trx_rhs ) { - if( trx_lhs.trx_id != trx_rhs.trx_id ) { - return false; - } - auto status3 = find_if_mismatched( - trx_lhs.actions.begin(), trx_lhs.actions.end(), - trx_rhs.actions.begin(), trx_rhs.actions.end(), - [&result, &block_lhs, &trx_lhs] - ( const action_data& act_lhs, const action_data& act_rhs ) { - if( std::tie( act_lhs.global_sequence_num, - act_lhs.receiver, - act_lhs.first_receiver, - act_lhs.action_name ) - != std::tie( act_rhs.global_sequence_num, - act_rhs.receiver, - act_rhs.first_receiver, - act_rhs.action_name ) - ) { - return false; - } - if( act_lhs.recorded_intrinsics != act_rhs.recorded_intrinsics ) { - result.emplace( intrinsic_differences{ - .block_num = block_lhs.block_num, - .trx_id = trx_lhs.trx_id, - .global_sequence_num = act_lhs.global_sequence_num, - .receiver = act_lhs.receiver, - .first_receiver = act_lhs.first_receiver, - .action_name = act_lhs.action_name, - .lhs_recorded_intrinsics = act_lhs.recorded_intrinsics, - .rhs_recorded_intrinsics = act_rhs.recorded_intrinsics - } ); - return false; - } - return true; - } - ); - if( status3 == mismatch_t::equivalent ) return true; - if( error_msg == nullptr && !result ) { - error_msg = "different actions within a particular transaction"; - } - return false; - } - ); - if( status2 == mismatch_t::equivalent ) return true; - if( error_msg == nullptr && !result ) { - error_msg = "different transactions in a particular block"; - } - return false; - } - ); - - if( status == mismatch_t::mismatch ) { - if( error_msg != nullptr ) { - FC_THROW_EXCEPTION( fc::exception, error_msg ); - } - return result; - } - - FC_ASSERT( status == mismatch_t::equivalent, "logs do not have the same blocks" ); - return {}; // the logs are equivalent - } - -} } /// eosio::chain diff --git a/libraries/chain/webassembly/wabt.cpp b/libraries/chain/webassembly/wabt.cpp index a73617e199a..d43cd07c0be 100644 --- a/libraries/chain/webassembly/wabt.cpp +++ b/libraries/chain/webassembly/wabt.cpp @@ -107,10 +107,4 @@ void wabt_runtime::immediately_exit_currently_running_module() { throw wasm_exit(); } -digest_type calc_memory_hash( const Memory& mem ) { - digest_type::encoder enc; - enc.write( mem.data.data(), mem.data.size() ); - return enc.result(); -} - }}}} diff --git a/libraries/chain/webassembly/wavm.cpp b/libraries/chain/webassembly/wavm.cpp index c0481c7cb86..71f72fa832f 100644 --- a/libraries/chain/webassembly/wavm.cpp +++ b/libraries/chain/webassembly/wavm.cpp @@ -171,13 +171,4 @@ void wavm_runtime::immediately_exit_currently_running_module() { #endif } -digest_type calc_memory_hash( MemoryInstance* mem ) { - const char* base_of_memory = (const char*)( getMemoryBaseAddress(mem) ); - std::size_t mem_size = IR::numBytesPerPage * Runtime::getMemoryNumPages(mem); - - digest_type::encoder enc; - enc.write( base_of_memory, mem_size ); - return enc.result(); -} - }}}} diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 87062c460b4..87b069f95a1 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -253,8 +253,6 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip #ifdef __linux__ ("database-hugepage-path", bpo::value>()->composing(), "Optional path for database hugepages when in \"locked\" mode (may specify multiple times)") #endif - ("debug-log-intrinsics", bpo::bool_switch()->default_value(false), - "enable logging of intrinsics for debugging purposes") ; // TODO: rate limiting @@ -639,11 +637,6 @@ void chain_plugin::plugin_initialize(const variables_map& options) { else my->blocks_dir = bld; } - /* - if( options.at( "debug-log-intrinsics" ).as() ) { - my->chain_config->intrinsic_debug_log_path = app().data_dir() / "intrinsics.log"; - } - */ protocol_feature_set pfs; { diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index 6cb1e5bd635..a40ef3b423c 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -3,4 +3,3 @@ add_subdirectory( cleos ) add_subdirectory( keosd ) add_subdirectory( eosio-launcher ) add_subdirectory( eosio-blocklog ) -add_subdirectory( intrinsic-log-util ) diff --git a/programs/intrinsic-log-util/CMakeLists.txt b/programs/intrinsic-log-util/CMakeLists.txt deleted file mode 100644 index 4697c35d47a..00000000000 --- a/programs/intrinsic-log-util/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -add_executable( intrinsic-log-util - main.cpp - print.cpp - diff.cpp -) - -if( UNIX AND NOT APPLE ) - set(rt_library rt ) -endif() - -find_package( Gperftools QUIET ) -if( GPERFTOOLS_FOUND ) - message( STATUS "Found gperftools; compiling intrinsic-log-util with TCMalloc") - list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -endif() - -target_include_directories(intrinsic-log-util PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - -target_link_libraries( intrinsic-log-util - PRIVATE appbase - PRIVATE eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) - -copy_bin( intrinsic-log-util ) -#install( TARGETS -# intrinsic-log-util -# -# RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} -# LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} -# ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} -#) diff --git a/programs/intrinsic-log-util/cli_parser.hpp b/programs/intrinsic-log-util/cli_parser.hpp deleted file mode 100644 index bdb41621bed..00000000000 --- a/programs/intrinsic-log-util/cli_parser.hpp +++ /dev/null @@ -1,834 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include -#include -#include - -//#include -#include - -namespace eosio::cli_parser { - - namespace bfs = boost::filesystem; - namespace bpo = boost::program_options; - - enum class subcommand_style { - only_contains_subcommands, - invokable_and_contains_subcommands, - terminal_subcommand_with_positional_arguments, - terminal_subcommand_without_positional_arguments - }; - - struct subcommand_description { - subcommand_description( const std::string& subcommand_name, const std::string& description ) - :subcommand_name( subcommand_name ) - ,description( description ) - {} - - std::string subcommand_name; - std::string description; - }; - - struct subcommand_descriptions { - std::vector descriptions; - bool requires_subcommand = false; - }; - - struct positional_description { - enum argument_restriction { - required, - optional, - repeating - }; - - positional_description( const std::string& positional_name, - const std::string& description, - argument_restriction restriction ) - :positional_name( positional_name ) - ,description( description ) - ,restriction( restriction ) - {} - - std::string positional_name; - std::string description; - argument_restriction restriction; - }; - - using positional_descriptions = std::vector; - - struct autocomplete_failure {}; - struct autocomplete_success {}; - struct parse_failure {}; - - struct parse_metadata { - parse_metadata( const std::vector& context ) - :subcommand_context( context ) - {} - - /** @pre subcommand_descriptions alternative of other_description must be present */ - void add_subcommand_description( const std::string& subcommand, const std::string& description ) - { - std::get( other_description ).descriptions.emplace_back( subcommand, description ); - } - - void set_positional_descriptions( const positional_descriptions& pos_desc ) { - other_description.emplace( pos_desc ); - } - - void set_positional_descriptions( positional_descriptions&& pos_desc ) { - other_description.emplace( std::move(pos_desc) ); - } - - std::vector subcommand_context; - std::string subcommand_desc; - std::shared_ptr opts_description; - std::variant other_description; - bool initialization_skipped = false; - }; - - namespace detail { - template struct overloaded : Ts... { using Ts::operator()...; }; - template overloaded(Ts...) -> overloaded; - - template - struct has_name : std::false_type {}; - - template - struct has_name : std::true_type {}; - - template - struct has_description : std::false_type {}; - - template - struct has_description : std::true_type {}; - - template - struct has_initialize : std::false_type {}; - - template - struct has_initialize().initialize( std::move( std::declval() ) ), 0)> - : std::true_type - {}; - - template - bool maybe_call_visitor( Visitor&& visitor, Args&&... args ) { - if constexpr( std::is_invocable_v ) { - std::forward(visitor)( std::forward(args)... ); - return true; - } else { - return false; - } - } - - template - bool reduce_tuple_helper( Ret& accumulator, Tuple&& t, - Extractor&& extract, Reducer&& reduce ) - { - if constexpr( I < std::tuple_size_v> ) { - if( !reduce( accumulator, I, extract( std::get( t ) ) ) ) - return false; - - return reduce_tuple_helper( accumulator, std::forward(t), - std::forward(extract), - std::forward(reduce) ); - } - - return true; - } - - template - bool reduce_tuple( Ret& accumulator, Tuple&& t, - Extractor&& extract, Reducer&& reduce ) - { - return reduce_tuple_helper<0>( accumulator, std::forward(t), - std::forward(extract), - std::forward(reduce) ); - } - - template - bool visit_by_name_helper( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { - using variant_type = std::variant; - - if constexpr( I < std::variant_size_v ) - { - if constexpr( has_name< std::variant_alternative_t >::value ) { - if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { - std::forward(visitor)( std::get( var ) ); - return true; - } - } - return visit_by_name_helper( var, - subcommand_name, - std::forward(visitor) ); - } else { - return false; - } - } - - template - bool visit_by_name( const std::variant& var, const std::string& subcommand_name, Visitor&& visitor ) { - return visit_by_name_helper<0>( var, subcommand_name, std::forward( visitor ) ); - } - - template - bool construct_variant_by_name_helper( Variant& var, - const std::string& subcommand_name, - Visitor&& visitor, - Args&&... args ) - { - if constexpr( I < std::variant_size_v ) { - if constexpr( has_name< std::variant_alternative_t >::value ) { - if( subcommand_name.compare( std::variant_alternative_t::name ) == 0 ) { - std::forward(visitor)( var.template emplace( std::forward(args)... ) ); - return true; - } - } - return construct_variant_by_name_helper( var, - subcommand_name, - std::forward(visitor), - std::forward(args)... ); - } else { - return false; - } - } - - template - bool construct_variant_by_name( Variant& var, - const std::string& subcommand_name, - Args&&... args ) - { - return construct_variant_by_name_helper<0>( var, - subcommand_name, - []( auto&& ) {}, // do nothing in visitor - std::forward(args)... ); - } - - template - bool construct_and_visit_variant_by_name( Variant& var, - const std::string& subcommand_name, - Visitor&& visitor, - Args&&... args ) - { - return construct_variant_by_name_helper<0>( var, - subcommand_name, - std::forward(visitor), - std::forward(args)... ); - } - - template - void static_visit_all_helper( Visitor&& visitor ) - { - if constexpr( I < std::variant_size_v ) { - if constexpr( has_name< std::variant_alternative_t >::value ) { - std::string desc; - if constexpr( has_description< std::variant_alternative_t >::value ) { - desc = std::variant_alternative_t::get_description(); - } - visitor( std::variant_alternative_t::name, desc ); - } - return static_visit_all_helper( std::forward(visitor) ); - } - } - - template - void static_visit_all( Visitor&& visitor ) { - static_visit_all_helper<0, Variant>( std::forward(visitor) ); - } - - struct no_subcommand {}; - - class subcommand_untyped_base {}; - - template - class subcommand_base : public subcommand_untyped_base { - static_assert( (std::is_default_constructible_v && ...) ); - static_assert( (std::is_base_of_v && ...), "subcommands must derive from subcommand type" ); - - protected: - subcommand_base() = default; - friend T; - - bpo::options_description _get_options( const std::vector& subcommand_context ) - { - std::string options_name; - if( subcommand_context.size() > 0 ) { - for( const auto& s : subcommand_context ) { - options_name += s; - options_name += " "; - } - } else { - options_name += T::name; - options_name += " "; - } - options_name += "options"; - - bpo::options_description options( options_name ); - T& derived = static_cast( *this ); - derived.set_options( options ); - return options; - } - - template - static std::string _get_description() { - if constexpr( has_description::value ) { - return U::get_description(); - } else { - return {}; - } - } - - template - void _initialize( bpo::variables_map&& vm ) { - if constexpr( has_initialize::value ) { - T& derived = static_cast( *this ); - derived.initialize( std::move(vm) ); - } - } - }; - - template - class subcommand; - - template - class subcommand - : public subcommand_base - { - protected: - using base_type = subcommand_base; - using subcommands_type = std::variant; - - using _enable = std::enable_if_t< - SubcommandStyle == subcommand_style::only_contains_subcommands - || SubcommandStyle == subcommand_style::invokable_and_contains_subcommands - >; - - static_assert( sizeof...(Us) > 0 ); - - subcommands_type _subcommand; - - public: - static constexpr bool has_subcommands = true; - static constexpr bool invokable_subcommand = (SubcommandStyle == subcommand_style::invokable_and_contains_subcommands); - - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - bpo::options_description subcommand_options{base_type::_get_options(subcommand_context)}; - - auto subcommand_options_ptr = std::make_shared( subcommand_options ); - - subcommand_options.add_options() - ("__command", bpo::value(), "command to execute" ) - ("__subargs", bpo::value>(), "arguments for command" ) - ; - - bpo::positional_options_description pos; - pos.add( "__command", 1 ).add( "__subargs", -1 ); - - bpo::parsed_options parsed = bpo::command_line_parser( opts ) - .options( subcommand_options ) - .positional( pos ) - .allow_unregistered() - .run(); - - bpo::variables_map vm; - - bpo::variables_map vm_temp; - bpo::store( parsed, vm_temp ); - - if( vm_temp.count( "__command" ) > 0 ) { - std::string cmd_name = vm_temp["__command"].as(); - - std::vector inner_opts = bpo::collect_unrecognized( parsed.options, bpo::include_positional ); - FC_ASSERT( inner_opts.size() > 0, "unexpected error" ); - FC_ASSERT( *inner_opts.begin() == cmd_name, - "unrecognized option '${option}'", - ("option", *inner_opts.begin()) - ); - inner_opts.erase( inner_opts.begin() ); - - bool success = construct_and_visit_variant_by_name( _subcommand, cmd_name, - [&inner_opts, &pm, &cmd_name, &subcommand_options_ptr, &skip_initialize, context = subcommand_context] - ( auto&& s ) mutable { - context.emplace_back( cmd_name ); - pm = s.parse( inner_opts, skip_initialize, context ); - FC_ASSERT( pm.opts_description, "unexpected error" ); - subcommand_options_ptr->add( *pm.opts_description ); - pm.opts_description = subcommand_options_ptr; - } - ); - FC_ASSERT( success, "unrecognized sub-command '${name}' within context: ${context}", - ("name", cmd_name)("context", subcommand_context) - ); - vm = std::move(vm_temp); - } else { - bpo::parsed_options parsed2 = bpo::command_line_parser( opts ) - .options( *subcommand_options_ptr ) - .run(); - bpo::store( parsed2, vm ); - - std::get( pm.other_description ).requires_subcommand = !invokable_subcommand; - - static_visit_all>( - [&pm]( const char* name, const std::string& desc ) { - pm.add_subcommand_description( name, desc ); - } - ); - - pm.opts_description = std::move(subcommand_options_ptr); - pm.subcommand_desc = base_type::_get_description(); - pm.initialization_skipped = skip_initialize; - } - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - - return pm; - } - - const subcommands_type& get_subcommand()const { return _subcommand; } - }; - - template - class subcommand - : public subcommand_base - { - protected: - using base_type = subcommand_base; - - public: - static constexpr bool has_subcommands = false; - static constexpr bool invokable_subcommand = true; - - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - pm.subcommand_desc = base_type::_get_description(); - pm.opts_description = std::make_shared( - base_type::_get_options(subcommand_context) ); - - bpo::options_description subcommand_options{ *pm.opts_description }; - positional_descriptions pos_desc; - const auto& pos = _get_positional_options( subcommand_options, pos_desc ); - pm.set_positional_descriptions( pos_desc ); - - bpo::variables_map vm; - - bpo::store( bpo::command_line_parser( opts ) - .options( subcommand_options ) - .positional( pos ) - .run(), - vm ); - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - pm.initialization_skipped = skip_initialize; - - return pm; - } - - protected: - bpo::positional_options_description _get_positional_options( bpo::options_description& options, - positional_descriptions& pos_desc ) - { - T& derived = static_cast( *this ); - return derived.get_positional_options( options, pos_desc ); - } - }; - - template - class subcommand - : public subcommand_base - { - protected: - using base_type = subcommand_base; - - public: - static constexpr bool has_subcommands = false; - static constexpr bool invokable_subcommand = true; - - - parse_metadata parse( const std::vector& opts, bool skip_initialize, - const std::vector& subcommand_context = std::vector() ) - { - parse_metadata pm( subcommand_context ); - pm.subcommand_desc = base_type::_get_description(); - pm.opts_description = std::make_shared( - base_type::_get_options(subcommand_context) ); - - bpo::variables_map vm; - - bpo::store( bpo::command_line_parser( opts ) - .options( *pm.opts_description ) - .run(), - vm ); - - if( !skip_initialize ) { - bpo::notify( vm ); - base_type::_initialize( std::move(vm) ); - } - pm.initialization_skipped = skip_initialize; - - return pm; - } - }; - - } /// namespace detail - - template - using subcommand = eosio::cli_parser::detail::subcommand; - - bpo::positional_options_description - build_positional_descriptions( const bpo::options_description& options, - positional_descriptions& pos_desc, - const std::vector& required_pos_names, - const std::vector& optional_pos_names = std::vector(), - const std::string& repeat_name = std::string{} - ) - { - bpo::positional_options_description pos; - - auto add_positional = [&pos, &pos_desc, &options]( const std::string& pos_name, - positional_description::argument_restriction restriction ) - { - pos.add( pos_name.c_str(), (restriction == positional_description::repeating ? -1 : 1) ); - const bpo::option_description* opt = options.find_nothrow( pos_name, false ); - if( opt ) { - pos_desc.emplace_back( pos_name, opt->description(), restriction ); - } else { - pos_desc.emplace_back( pos_name, "", restriction ); - } - }; - - for( const auto& n : required_pos_names ) { - add_positional( n, positional_description::required ); - } - - for( const auto& n : optional_pos_names ) { - add_positional( n, positional_description::optional ); - } - - if( repeat_name.size() > 0 ) { - add_positional( repeat_name, positional_description::repeating ); - } - - return pos; - } - - void print_subcommand_help( std::ostream& stream, const std::string& program_name, const parse_metadata& pm ) { - const subcommand_descriptions* subcommands = std::get_if( &pm.other_description ); - bool subcommands_present = (subcommands && subcommands->descriptions.size() > 0); - - const positional_descriptions* positionals = std::get_if( &pm.other_description ); - bool positionals_present = (positionals && positionals->size() > 0); - - auto formatting_width = pm.opts_description->get_option_column_width() - 2; - - std::string subcommand_usage; - for( const auto& s : pm.subcommand_context ) { - subcommand_usage += " "; - subcommand_usage += s; - } - if( subcommands_present ) { - if( subcommands->requires_subcommand ) - subcommand_usage += " SUBCOMMAND"; - else - subcommand_usage += " [SUBCOMMAND]"; - } - - if( pm.subcommand_desc.size() > 0 ) { - stream << pm.subcommand_desc << std::endl; - } - - stream << "Usage: " << program_name - << subcommand_usage - << " [OPTIONS]"; - - if( positionals_present ) { - for( const auto& p : *positionals ) { - bool optional_arg = (p.restriction == positional_description::optional); - stream << " "; - if( optional_arg ) stream << "["; - stream << p.positional_name; - if( optional_arg ) stream << "]"; - if( p.restriction == positional_description::repeating ) stream << "..."; - } - } - - stream << std::endl << std::endl; - - if( positionals_present ) { - stream << "Positionals:" << std::endl; - - for( const auto& p : *positionals ) { - stream << " "; - auto orig_width = stream.width( formatting_width ); - stream << std::left << p.positional_name; - stream.width( orig_width ); - stream << p.description << std::endl; - } - - stream << std::endl; - } - - pm.opts_description->print( stream ); - - if( subcommands_present ) { - stream << std::endl; - - std::string subcommands_title; - if( pm.subcommand_context.size() > 0 ) { - for( const auto& s : pm.subcommand_context ) { - subcommands_title += s; - subcommands_title += " "; - } - subcommands_title += "subcommands:"; - } else { - subcommands_title = "Subcommands:"; - } - - stream << subcommands_title << std::endl; - for( const auto& s : subcommands->descriptions ) { - stream << " "; - auto orig_width = stream.width( formatting_width ); - stream << std::left << s.subcommand_name; - stream.width( orig_width ); - stream << s.description << std::endl; - } - } - } - - bool print_autocomplete_suggestions( std::ostream& stream, const parse_metadata& pm, const std::string& last_token ) { - bool printed_something = false; - - if( std::holds_alternative( pm.other_description ) ) { - for( const auto& s : std::get( pm.other_description ).descriptions ) { - if( s.subcommand_name.find( last_token ) != 0 ) continue; - if( printed_something ) - stream << " "; - stream << s.subcommand_name; - printed_something = true; - } - } - - if( last_token.size() > 0 ) { - for( const auto& opt : pm.opts_description->options() ) { - //if( opt->match( last_token, true, false, false ) == bpo::option_description::no_match ) continue; - // The option_description::match function doesn't seem to work. - - const auto& short_name = opt->canonical_display_name( bpo::command_line_style::allow_dash_for_short ); - if( short_name.size() > 0 && short_name[0] == '-' && short_name.find( last_token ) == 0 ) { - if( printed_something ) - stream << " "; - stream << short_name; - printed_something = true; - } - - const auto& long_name = opt->canonical_display_name( bpo::command_line_style::allow_long ); - if( long_name.size() > 0 && long_name[0] == '-' && long_name.find( last_token ) == 0 ) { - if( printed_something ) - stream << " "; - stream << long_name; - printed_something = true; - } - } - } - - return printed_something; - } - - template - std::optional - parse_program_options( T& root, const std::vector& opts, - bool skip_initialize = false, bool avoid_throwing = false ) - { - try { - return root.parse( opts, skip_initialize ); - } catch( ... ) { - if( !avoid_throwing ) throw; - } - - return {}; - } - - template - std::variant - parse_program_options_extended( T& root, int argc, const char** argv, - std::ostream& out_stream, std::ostream* err_stream = nullptr, - bool avoid_throwing = false ) - { - if( argc <= 0 ) { - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "command line arguments should at least include program name" ); - } - - std::string program_name{ bfs::path( argv[0] ).filename().generic_string() }; - - bool autocomplete_mode = false; - - std::vector opts; - opts.reserve( argc - 1 ); - for( int i = 1; i < argc; ++i ) { - opts.emplace_back( std::string( *(argv + i) ) ); - if( opts.back().compare( "-" ) == 0 ) { - if( autocomplete_mode ) return autocomplete_failure{}; - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "- is not allowed by itself in the command line arguments" ); - } - if( opts.back().compare( "--" ) == 0 ) { - if( i != 1 ) { - if( autocomplete_mode ) return autocomplete_failure{}; - if( avoid_throwing ) return parse_failure{}; - FC_ASSERT( false, "-- is not allowed by itself in the command line arguments" ); - } - opts.pop_back(); - autocomplete_mode = true; - } - } - - if( !autocomplete_mode ) { - const auto pm = parse_program_options( root, opts, false, avoid_throwing ); - if( !pm ) return parse_failure{}; - return *pm; - } - - // Autocomplete helper logic: - - std::string last_token; - if( opts.size() > 0 ) { - if( opts.back().size() == 0 ) { - opts.pop_back(); - } else { - last_token = opts.back(); - } - } - - if( last_token.size() > 0 ) { - if( opts.size() == 0 ) return autocomplete_failure{}; - opts.pop_back(); - } - - auto pm = parse_program_options( root, opts, true, true ); - if( !pm ) return autocomplete_failure{}; - - if( last_token.size() > 0 ) { - if( print_autocomplete_suggestions( out_stream, *pm, last_token ) ) { - out_stream << std::endl; - } else { - return autocomplete_failure{}; - } - } - - if( err_stream ) { - *err_stream << std::endl; - print_subcommand_help( *err_stream, program_name, *pm ); - } - - return autocomplete_success{}; - } - - enum class dispatch_status { - successful_dispatch, - no_handler_for_terminal_subcommand, - no_handler_for_nonterminal_subcommand - }; - - namespace detail { - - template - auto dispatch_helper( Reducer&& reduce, Visitor&& visitor, const LastArg& last_arg, const OtherArgs&... other_args ) - -> std::enable_if_t< - !std::is_same_v, no_subcommand>, - std::pair - > - { - static_assert( std::is_convertible_v>, Ret> ); - - dispatch_status status = dispatch_status::no_handler_for_terminal_subcommand; - - if constexpr( LastArg::has_subcommands ) { - return std::visit([&]( const auto& next_subcommand ) { - return dispatch_helper( std::forward(reduce), - std::forward(visitor), - next_subcommand, - other_args..., last_arg ); - }, last_arg.get_subcommand() ); - } else { - if( maybe_call_visitor( std::forward(visitor), other_args..., last_arg ) ) - status = dispatch_status::successful_dispatch; - } - - return {status, reduce( std::make_tuple( std::cref(other_args)..., std::cref(last_arg) ) )}; - } - - template - auto dispatch_helper( Reducer&& reduce, Visitor&& visitor, const LastArg& last_arg, const OtherArgs&... other_args ) - -> std::enable_if_t< - std::is_same_v, no_subcommand>, - std::pair - > - { - static_assert( std::is_convertible_v>, Ret> ); - - dispatch_status status = dispatch_status::no_handler_for_nonterminal_subcommand; - - if( maybe_call_visitor( std::forward(visitor), other_args... ) ) - status = dispatch_status::successful_dispatch; - - return {status, reduce( std::make_tuple( std::cref(other_args)... ) )}; - } - - template - std::string get_subcommand_string( const std::tuple& t ) { - static_assert( (has_name::value && ...) ); - - std::string result; - - bool completed = reduce_tuple( result, t, - []( auto&& v ) -> std::string { return std::decay_t::name; }, - []( std::string& accumulator, std::size_t index, const std::string& v ) -> bool { - if( index != 0 ) { - if( index > 1 ) { - accumulator += " "; - } - accumulator += v; - } - return true; - } - ); - - if( !completed ) return {}; - - return result; - } - - } /// namespace detail - - template - auto dispatch( Dispatcher&& dispatcher, const Root& root ) -> std::pair { - return detail::dispatch_helper( - []( const auto& t ) { return detail::get_subcommand_string(t); }, - std::forward(dispatcher), - root - ); - } - -} /// namespace eosio::cli_parser - -namespace eosio { - using subcommand_style = cli_parser::subcommand_style; - - template - using subcommand = eosio::cli_parser::subcommand; -} diff --git a/programs/intrinsic-log-util/diff.cpp b/programs/intrinsic-log-util/diff.cpp deleted file mode 100644 index 72d22ce33a3..00000000000 --- a/programs/intrinsic-log-util/diff.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "diff.hpp" - -#include -#include - -#include - -void exec_diff( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::diff& diff ) -{ - dispatcher.stream << "diff was called" << std::endl; - idump((diff.first_log.generic_string())(diff.second_log.generic_string())); -} diff --git a/programs/intrinsic-log-util/diff.hpp b/programs/intrinsic-log-util/diff.hpp deleted file mode 100644 index a8654d56b71..00000000000 --- a/programs/intrinsic-log-util/diff.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "root.hpp" - -namespace configuration { - struct root; - - struct diff { - bfs::path first_log; - bfs::path second_log; - }; -} - -void exec_diff( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::diff& diff ); diff --git a/programs/intrinsic-log-util/intrinsic_log.hpp b/programs/intrinsic-log-util/intrinsic_log.hpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/programs/intrinsic-log-util/main.cpp b/programs/intrinsic-log-util/main.cpp deleted file mode 100644 index e5c5550fcd0..00000000000 --- a/programs/intrinsic-log-util/main.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/** - * @file - * @copyright defined in eosio/LICENSE.txt - */ -#include "root.hpp" -#include "diff.hpp" -#include "print.hpp" - -#include "cli_parser.hpp" - -#include - -#include - -#include -#include - -#include -#include - -using namespace eosio; -using namespace eosio::chain; - -namespace bpo = boost::program_options; - -struct print_block_subcommand - : public configuration::print_block, - public subcommand< - print_block_subcommand, - subcommand_style::terminal_subcommand_with_positional_arguments - > -{ - static constexpr const char* name = "block"; - - static std::string get_description() { - return "Print a specific block"; - } - - void set_options( bpo::options_description& options ) { - options.add_options() - ("output-file,o", bpo::value(), - "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") - ; - } - - bpo::positional_options_description - get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc ) { - options.add_options() - ("block-num", bpo::value(&block_num), - "the block number to print") - ("trx-id", bpo::value>(), - "ID of transaction to print (optional)") - ; - - return cli_parser::build_positional_descriptions( options, pos_desc, { "block-num" }, {}, "trx-id" ); - } - - void initialize( bpo::variables_map&& vm ) { - if( vm.count( "trx-id" ) ) { - for( const auto& trx_id : vm.at( "trx-id" ).as>() ) { - try { - trxs_filter.emplace_back( fc::variant( trx_id ).as() ); - } EOS_RETHROW_EXCEPTIONS( fc::exception, "invalid transaction id: ${trx_id}", ("trx_id", trx_id) ) - } - } - } -}; - -struct print_blocks_subcommand - : public configuration::print_blocks, - public subcommand< - print_blocks_subcommand, - subcommand_style::terminal_subcommand_without_positional_arguments - > -{ - static constexpr const char* name = "blocks"; - - static std::string get_description() { - return "Print a range of blocks"; - } - - void set_options( bpo::options_description& options ) { - options.add_options() - ("output-file,o", bpo::value(), - "the file to write the output to (absolute or relative path). If not specified then output is to stdout.") - ("first,f", bpo::value(&first_block_num)->default_value(first_block_num), - "the first block number to print") - ("last,l", bpo::value()->default_value(last_block_num), - "the first block number to print") - ; - } - - void initialize( bpo::variables_map&& vm ) { - if( vm.count( "last" ) ) { - last_block_num = vm.at( "last" ).as(); - FC_ASSERT( last_block_num >= first_block_num, "invalid range" ); - } - } -}; - -struct print_subcommand - : public configuration::print, - public subcommand< - print_subcommand, - subcommand_style::only_contains_subcommands, - print_block_subcommand, - print_blocks_subcommand - > -{ - static constexpr const char* name = "print"; - - static std::string get_description() { - return "Print blocks in a log file"; - } - - void set_options( bpo::options_description& options ) { - options.add_options() - ("log-file", bpo::value(), "path to the log file (required)") - ("no-pretty-print", bpo::bool_switch()->default_value(no_pretty_print), "avoid pretty printing") - ; - } - - void initialize( bpo::variables_map&& vm ) { - FC_ASSERT( vm.count( "log-file" ) > 0, "path to log file must be provided (use --log-file)" ); - log_path = bfs::canonical( vm.at( "log-file" ).as() ); - } -}; - -struct diff_subcommand - : public configuration::diff, - public subcommand< - diff_subcommand, - subcommand_style::terminal_subcommand_with_positional_arguments - > -{ - static constexpr const char* name = "diff"; - - static std::string get_description() { - return "Find the differences between two log files"; - } - - void set_options( bpo::options_description& options ) { - options.add_options() - ("start-block", bpo::value()->default_value(0), - "ignore any differences prior to this block") - ; - } - - bpo::positional_options_description - get_positional_options( bpo::options_description& options, cli_parser::positional_descriptions& pos_desc ) { - options.add_options() - ("first-log", bpo::value(&first_log), - "path to the first log file to compare") - ("second-log", bpo::value(&second_log), - "path to the second log file to compare") - ; - - return cli_parser::build_positional_descriptions( options, pos_desc, { - "first-log", "second-log" - } ); - } - - void initialize( bpo::variables_map&& vm ) { - first_log = bfs::canonical( first_log ); - second_log = bfs::canonical( second_log ); - } -}; - -struct root_command - : public configuration::root, - public subcommand< - root_command, - subcommand_style::only_contains_subcommands, - print_subcommand, - diff_subcommand - > -{ - static constexpr const char* name = "Generic"; - - static std::string get_description() { - return "Utility to inspect intrinsic log files"; - } - - void set_options( bpo::options_description& options ) { - options.add_options() - ("help,h", bpo::bool_switch(&print_help)->default_value(print_help), "Print this help message and exit.") - ("no-detail", bpo::bool_switch()->default_value(no_detail), "Temporarily added for testing purposes.") - ; - } -}; - -class subcommand_dispatcher : public subcommand_dispatcher_base { -public: - subcommand_dispatcher( std::ostream& stream ) - :subcommand_dispatcher_base( stream ) - {} - - void operator()( const root_command& root, const diff_subcommand& diff ) { - exec_diff( *this, root, diff ); - } - - void operator()( const root_command& root, const print_subcommand& print, const print_blocks_subcommand& blocks ) { - exec_print_blocks( *this, root, print, blocks ); - } - - void operator()( const root_command& root, const print_subcommand& print, const print_block_subcommand& block ) { - exec_print_block( *this, root, print, block ); - } -}; - -int main( int argc, const char** argv ) { - try { - root_command rc; - auto parse_result = cli_parser::parse_program_options_extended( rc, argc, argv, std::cout, &std::cerr ); - - if( std::holds_alternative( parse_result ) ) { - FC_ASSERT( false, "parse error occurred" ); - } else if( std::holds_alternative( parse_result ) ) { - return -1; - } else if( std::holds_alternative( parse_result ) ) { - return 0; - } - - const auto& pm = std::get( parse_result ); - - if( argc == 1 || rc.print_help ) { - cli_parser::print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); - return 0; - } - - std::ios::sync_with_stdio(false); // for potential performance boost for large log files - - subcommand_dispatcher d( std::cout ); - auto dispatch_result = cli_parser::dispatch( d, rc ); - - FC_ASSERT( dispatch_result.first != cli_parser::dispatch_status::no_handler_for_terminal_subcommand, - "The ${subcommand} sub-command has not been implemented yet.", - ("subcommand", dispatch_result.second) - ); - - if( dispatch_result.first == cli_parser::dispatch_status::no_handler_for_nonterminal_subcommand ) { - std::cout << "\033[0;31m" - << "The sub-command " - << "\033[1;31m" << dispatch_result.second << "\033[0;31m" - << " cannot be called by itself." - << "\033[0m" - << std::endl; - cli_parser::print_subcommand_help( std::cout, bfs::path( argv[0] ).filename().generic_string(), pm ); - return -1; - } - - } catch( const fc::exception& e ) { - elog( "${e}", ("e", e.to_detail_string())); - return -1; - } catch( const boost::exception& e ) { - elog("${e}", ("e",boost::diagnostic_information(e))); - return -1; - } catch( const std::exception& e ) { - elog("${e}", ("e",e.what())); - return -1; - } catch( ... ) { - elog("unknown exception"); - return -1; - } - - return 0; -} diff --git a/programs/intrinsic-log-util/print.cpp b/programs/intrinsic-log-util/print.cpp deleted file mode 100644 index f5483bc9af0..00000000000 --- a/programs/intrinsic-log-util/print.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "print.hpp" - -#include -#include - -#include - -void exec_print_block( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::print& print, - const configuration::print_block& block ) -{ - dispatcher.stream << "print block was called" << std::endl; - idump((print.log_path.generic_string())(block.block_num)(block.trxs_filter)); -} - -void exec_print_blocks( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::print& print, - const configuration::print_blocks& blocks ) -{ - dispatcher.stream << "print blocks was called" << std::endl; - idump((print.log_path.generic_string())(blocks.first_block_num)(blocks.last_block_num)); -} diff --git a/programs/intrinsic-log-util/print.hpp b/programs/intrinsic-log-util/print.hpp deleted file mode 100644 index 7eb32cab8a0..00000000000 --- a/programs/intrinsic-log-util/print.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "root.hpp" - -#include - -namespace configuration { - struct print { - bfs::path log_path; - bool no_pretty_print = false; - }; - - struct print_block { - uint32_t block_num = 0; - std::vector trxs_filter; - }; - - struct print_blocks { - uint32_t first_block_num = 0; - uint32_t last_block_num = std::numeric_limits::max(); - }; - -} - -void exec_print_block( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::print& print, - const configuration::print_block& block ); - -void exec_print_blocks( subcommand_dispatcher_base& dispatcher, - const configuration::root& root, - const configuration::print& print, - const configuration::print_blocks& blocks ); diff --git a/programs/intrinsic-log-util/root.hpp b/programs/intrinsic-log-util/root.hpp deleted file mode 100644 index 0f17ade8d88..00000000000 --- a/programs/intrinsic-log-util/root.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -namespace bfs = boost::filesystem; - -namespace configuration { - struct root { - bool print_help = false; - bool no_detail = false; - }; -} - -class subcommand_dispatcher_base { -public: - subcommand_dispatcher_base( std::ostream& stream ) - :stream( stream ) - {} - -public: - std::ostream& stream; -}; diff --git a/unittests/intrinsic_debug_log_tests.cpp.bak b/unittests/intrinsic_debug_log_tests.cpp.bak deleted file mode 100644 index 399d1eeb7d7..00000000000 --- a/unittests/intrinsic_debug_log_tests.cpp.bak +++ /dev/null @@ -1,352 +0,0 @@ -/** - * @file - * @copyright defined in eos/LICENSE - */ -#include - -#include -#include -#include -#include - -using namespace eosio; -using namespace chain; - -BOOST_AUTO_TEST_SUITE(intrinsic_debug_log_tests) - -BOOST_AUTO_TEST_CASE(basic_test) { - fc::temp_directory tempdir; - auto log_path = tempdir.path() / "intrinsic.log"; - { - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - name alice("alice"); - name foo("foo"); - intrinsic_debug_log log( log_path ); - log.open(); - log.start_block( 2u ); - log.start_transaction( transaction_id_type() ); - log.start_action( 1ull, alice, alice, foo ); - log.acknowledge_intrinsic_without_recording(); - log.record_intrinsic( digest, digest ); - log.finish_block(); - log.close(); - } - - std::fstream log_stream( log_path.generic_string().c_str(), std::ios::in | std::ios::binary ); - log_stream.seekg( 0, std::ios::end ); - std::size_t file_size = log_stream.tellg(); - log_stream.seekg( 0, std::ios::beg ); - - vector buffer( file_size ); - log_stream.read( buffer.data(), file_size ); - log_stream.close(); - - const char* expected = - "00" "02000000" // start of block 2 - "01" "0000000000000000000000000000000000000000000000000000000000000000" // start of transaction - "02" // start of action with: - "0100000000000000" // global sequence number of 1, - "0000000000855c34" // receiver "alice", - "0000000000855c34" // first_receiver "alice", - "000000000000285d" // and action_name "foo". - "03" // recording an intrinsic with: - "01000000" // an ordinal of 1, - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" // this arguments hash, - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" // and this WASM lineary memory hash. - "00" "0000000000000000" // end of the block with pointer to position of the start of the block - ; - - BOOST_CHECK_EQUAL( fc::to_hex( buffer ), expected ); -} - -BOOST_AUTO_TEST_CASE(iterate_test) { - fc::temp_directory tempdir; - auto log_path = tempdir.path() / "intrinsic.log"; - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - name alice("alice"); - name bob("bob"); - name foo("foo"); - name bar("bar"); - - intrinsic_debug_log log( log_path ); - log.open(); - log.close(); - - log.open( intrinsic_debug_log::open_mode::read_only ); - BOOST_CHECK( log.begin_block() == log.end_block() ); - log.close(); - - log.open(); - log.start_block( 2u ); - log.start_transaction( transaction_id_type() ); - log.start_action( 1ull, alice, alice, foo ); - log.acknowledge_intrinsic_without_recording(); - log.record_intrinsic( digest, digest ); - log.finish_block(); - log.close(); - - log.open( intrinsic_debug_log::open_mode::read_only ); - BOOST_CHECK( log.begin_block() != log.end_block() ); - log.close(); - - log.open(); - log.start_block( 4u ); - log.start_transaction( transaction_id_type() ); - log.start_transaction( transaction_id_type() ); - log.start_action( 2ull, alice, alice, bar ); - log.record_intrinsic( digest, digest ); - log.record_intrinsic( digest, digest ); - log.start_action( 3ull, bob, alice, bar ); - log.finish_block(); - log.close(); - - log.open( intrinsic_debug_log::open_mode::read_only ); - std::vector< std::pair< uint32_t, uint32_t > > expected_blocks = { - {2u, 1u}, - {4u, 2u} - }; - - { - auto block_itr = log.begin_block(); - const auto block_end_itr = log.end_block(); - for( auto itr = expected_blocks.begin(); block_itr != block_end_itr; ++block_itr, ++itr ) { - BOOST_REQUIRE( itr != expected_blocks.end() ); - BOOST_CHECK_EQUAL( block_itr->block_num, itr->first ); - BOOST_CHECK_EQUAL( block_itr->transactions.size(), itr->second ); - wdump( (fc::json::to_pretty_string(*block_itr)) ); - } - - for( auto itr = expected_blocks.rbegin(); itr != expected_blocks.rend(); ++itr ) { - --block_itr; - BOOST_CHECK_EQUAL( block_itr->block_num, itr->first ); - BOOST_CHECK_EQUAL( block_itr->transactions.size(), itr->second ); - } - log.close(); // invalidates block_itr; - } - - log.open( intrinsic_debug_log::open_mode::read_only ); - { - auto block_itr = log.end_block(); - --block_itr; - auto itr = expected_blocks.rbegin(); - for( ; itr != expected_blocks.rend() && itr->first > 3u; ++itr ) { - --block_itr; - // Avoid dereferencing block_itr to not read block and add to cache. - } - - BOOST_REQUIRE( itr != expected_blocks.rend() ); - BOOST_REQUIRE_EQUAL( itr->first, 2u ); // ensure we are at the expected block - - // block_itr should also be point to a valid block (the same one with a block height of 2). - // However, it hasn't been cached yet since we have not yet dereferenced it. - - BOOST_REQUIRE_EQUAL( block_itr->block_num, 2u ); - } -} - -BOOST_AUTO_TEST_CASE(difference_test) { - fc::temp_directory tempdir; - auto log1_path = tempdir.path() / "intrinsic1.log"; - auto log2_path = tempdir.path() / "intrinsic2.log"; - auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - name alice("alice"); - name bob("bob"); - name foo("foo"); - name bar("bar"); - - intrinsic_debug_log log1( log1_path ); - { - log1.open(); - log1.start_block( 2u ); - log1.start_transaction( transaction_id_type() ); - log1.start_action( 1ull, alice, alice, foo ); - log1.acknowledge_intrinsic_without_recording(); - log1.record_intrinsic( digest, digest ); - log1.finish_block(); - log1.start_block( 4u ); - log1.start_transaction( trx_id ); - log1.start_action( 2ull, alice, alice, bar ); - log1.record_intrinsic( digest, digest ); - log1.record_intrinsic( digest, digest ); - log1.start_action( 3ull, bob, alice, bar ); - log1.finish_block(); - log1.close(); - } - - intrinsic_debug_log log2( log2_path ); - { - log2.open(); - log2.start_block( 2u ); - log2.start_transaction( transaction_id_type() ); - log2.start_action( 1ull, alice, alice, foo ); - log2.acknowledge_intrinsic_without_recording(); - log2.record_intrinsic( digest, digest ); - log2.finish_block(); - log2.start_block( 4u ); - log2.start_transaction( trx_id ); - log2.start_action( 2ull, alice, alice, bar ); - log2.record_intrinsic( digest, digest ); - log2.start_action( 3ull, bob, alice, bar ); - log2.finish_block(); - log2.close(); - } - - auto result = intrinsic_debug_log::find_first_difference( log1, log2 ); - BOOST_REQUIRE( result.has_value() ); - - wdump( (fc::json::to_pretty_string(*result)) ); - - BOOST_CHECK_EQUAL( result->block_num, 4u ); - BOOST_CHECK_EQUAL( result->trx_id, trx_id ); - BOOST_CHECK_EQUAL( result->global_sequence_num, 2ull ); - BOOST_CHECK_EQUAL( result->receiver.to_string(), "alice" ); - BOOST_CHECK_EQUAL( result->first_receiver.to_string(), "alice" ); - BOOST_CHECK_EQUAL( result->action_name.to_string(), "bar" ); - BOOST_CHECK_EQUAL( result->lhs_recorded_intrinsics.size(), 2 ); - BOOST_CHECK_EQUAL( result->rhs_recorded_intrinsics.size(), 1 ); -} - -BOOST_AUTO_TEST_CASE(equivalence_test) { - fc::temp_directory tempdir; - auto log1_path = tempdir.path() / "intrinsic1.log"; - auto log2_path = tempdir.path() / "intrinsic2.log"; - auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - name alice("alice"); - name bob("bob"); - name foo("foo"); - name bar("bar"); - - intrinsic_debug_log log1( log1_path ); - { - log1.open(); - log1.start_block( 1u ); - log1.finish_block(); - log1.start_block( 2u ); - log1.start_transaction( transaction_id_type() ); - log1.abort_transaction(); - log1.start_transaction( transaction_id_type() ); - log1.start_action( 1ull, alice, alice, foo ); - log1.acknowledge_intrinsic_without_recording(); - log1.record_intrinsic( digest, digest ); - log1.finish_block(); - log1.start_block( 4u ); - log1.start_transaction( trx_id ); - log1.start_action( 2ull, alice, alice, bar ); - log1.record_intrinsic( digest, digest ); - log1.start_action( 3ull, bob, alice, bar ); - log1.finish_block(); - log1.start_block( 5u ); - log1.start_transaction( transaction_id_type() ); - log1.start_action( 4ull, alice, alice, foo ); - log1.abort_block(); - log1.start_block( 5u ); - log1.close(); - } - - intrinsic_debug_log log2( log2_path ); - { - log2.open(); - log2.start_block( 2u ); - log2.start_transaction( transaction_id_type() ); - log2.start_action( 1ull, alice, alice, foo ); - log2.acknowledge_intrinsic_without_recording(); - log2.record_intrinsic( digest, digest ); - log2.finish_block(); - log2.start_block( 4u ); - log2.start_transaction( trx_id ); - log2.start_action( 2ull, alice, alice, bar ); - log2.record_intrinsic( digest, digest ); - log2.start_action( 3ull, bob, alice, bar ); - log2.finish_block(); - log2.close(); - } - - auto result = intrinsic_debug_log::find_first_difference( log1, log2 ); - if( result ) { - wdump( (fc::json::to_pretty_string(*result)) ); - } - BOOST_REQUIRE( !result ); -} - -BOOST_AUTO_TEST_CASE(auto_finish_block_test) { - fc::temp_directory tempdir; - auto ref_log1_path = tempdir.path() / "intrinsic1.log"; - auto ref_log2_path = tempdir.path() / "intrinsic2.log"; - auto log_path = tempdir.path() / "intrinsic.log"; - auto trx_id = fc::variant("0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10").as(); - auto digest = fc::variant("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20").as(); - name alice("alice"); - name foo("foo"); - - intrinsic_debug_log ref_log1( ref_log1_path ); // reference log 1 - { - ref_log1.open(); - ref_log1.start_block( 1u ); - ref_log1.finish_block(); - ref_log1.close(); - } - - intrinsic_debug_log ref_log2( ref_log2_path ); // reference log 2 - { - ref_log2.open(); - ref_log2.start_block( 1u ); - ref_log2.finish_block(); - ref_log2.start_block( 2u ); - ref_log2.start_transaction( trx_id ); - ref_log2.start_action( 1ull, alice, alice, foo ); - ref_log2.acknowledge_intrinsic_without_recording(); - ref_log2.record_intrinsic( digest, digest ); - ref_log2.finish_block(); - ref_log2.close(); - } - - // Mode 1 (default mode): do not automatically finish any pending block. - // This mode enforces that the log remains consistent with block activity (truncate blocks if necessary). - { - { - intrinsic_debug_log log( log_path ); - log.open(); - log.start_block( 1u ); - log.finish_block(); - log.start_block( 2u ); - log.start_transaction( trx_id ); - log.start_action( 1ull, alice, alice, foo ); - log.acknowledge_intrinsic_without_recording(); - log.record_intrinsic( digest, digest ); - } - - intrinsic_debug_log log( log_path ); - log.open( intrinsic_debug_log::open_mode::read_only ); - - auto result = intrinsic_debug_log::find_first_difference( log, ref_log1 ); - BOOST_REQUIRE( !result ); - } - - // Mode 2: automatically finish any pending block. - // This mode keeps recent information in log during any shutdown by automatically finish the block even if it - // is not explicitly called. This may result in the data recorded for last block in the log to not have all - // the data that actually occurred in that block. - { - { - intrinsic_debug_log log( log_path ); - log.open( intrinsic_debug_log::open_mode::continue_existing_and_auto_finish_block ); - log.start_block( 1u ); - log.finish_block(); - log.start_block( 2u ); - log.start_transaction( trx_id); - log.start_action( 1ull, alice, alice, foo ); - log.acknowledge_intrinsic_without_recording(); - log.record_intrinsic( digest, digest ); - } - - intrinsic_debug_log log( log_path ); - log.open( intrinsic_debug_log::open_mode::read_only ); - - auto result = intrinsic_debug_log::find_first_difference( log, ref_log2 ); - BOOST_REQUIRE( !result ); - } -} - -BOOST_AUTO_TEST_SUITE_END() From bb454802d7ae11f109c14c5968e18e5e9a9de4d2 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 23 Sep 2019 11:12:17 -0400 Subject: [PATCH 81/88] Cleanup --- libraries/chain/include/eosio/chain/wasm_interface.hpp | 4 ++-- .../chain/include/eosio/chain/wasm_interface_private.hpp | 3 --- libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp | 2 +- libraries/chain/webassembly/eos-vm.cpp | 4 ---- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 2007e65573f..2e59299b128 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -87,9 +87,9 @@ namespace eosio { namespace chain { //validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints static void validate(const controller& control, const bytes& code); - + //get the wasm_allocator used for the linear memory for wasm - static vm::wasm_allocator* get_wasm_allocator(); + static vm::wasm_allocator* get_wasm_allocator(); //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index a26def5fb00..40dd98adae4 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -15,13 +15,10 @@ #include "Runtime/Intrinsics.h" #include "Platform/Platform.h" #include "WAST/WAST.h" - #include "IR/Validate.h" #include -#include - using namespace fc; using namespace eosio::chain::webassembly; using namespace eosio::vm; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 9c050d0b9a8..b62e77b90c4 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -91,7 +91,7 @@ namespace eosio { namespace vm { struct construct_derived { static auto &value(Ctx& ctx) { return ctx.trx_context; } }; - + template <> struct construct_derived { static auto &value(eosio::chain::apply_context& ctx) { return ctx; } diff --git a/libraries/chain/webassembly/eos-vm.cpp b/libraries/chain/webassembly/eos-vm.cpp index 0fd07b83e57..94fc1f7cd79 100644 --- a/libraries/chain/webassembly/eos-vm.cpp +++ b/libraries/chain/webassembly/eos-vm.cpp @@ -2,7 +2,6 @@ #include #include #include -#include //eos-vm includes #include @@ -107,9 +106,6 @@ bool eos_vm_runtime::inject_module(IR::Module& module) { template std::unique_ptr eos_vm_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector) { using backend_t = backend; - std::ofstream mf("temp.wasm"); - mf.write((char*)code_bytes, code_size); - mf.close(); try { wasm_code_ptr code((uint8_t*)code_bytes, code_size); std::unique_ptr bkend = std::make_unique(code, code_size); From 5100c35a1d76b61b0d8cc1083d6a91299f93177a Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 23 Sep 2019 15:45:37 -0400 Subject: [PATCH 82/88] Reenable test that was commented out for some reason. --- unittests/api_tests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index a59ef384744..7f40f291ef2 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -587,7 +587,6 @@ BOOST_FIXTURE_TEST_CASE(require_notice_tests, TESTER) { try { } FC_LOG_AND_RETHROW() } -/* BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try { fc::temp_directory tempdir; validating_tester chain( tempdir, true ); @@ -615,7 +614,7 @@ BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try { BOOST_REQUIRE_EQUAL( chain.validate(), true ); } FC_LOG_AND_RETHROW() } -*/ + /************************************************************************************* * context free action tests *************************************************************************************/ From 0e039c179cb899b00d15cf491ac5415e10a9f8e0 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Mon, 23 Sep 2019 18:23:30 -0400 Subject: [PATCH 83/88] Update eos-vm --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 3a25cc10506..d09208662eb 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 3a25cc10506be4a09b87066382d9cf8b5c9c77c5 +Subproject commit d09208662eb5d313eb732dd00aafd733ba8fa9a3 From 1d785c174feb099133850dd1d74ee74b993a93b5 Mon Sep 17 00:00:00 2001 From: Bucky Kittinger Date: Tue, 24 Sep 2019 01:00:50 -0400 Subject: [PATCH 84/88] fix whitespace issue and update eos-vm --- libraries/chain/CMakeLists.txt | 2 +- libraries/eos-vm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 66bf34c60ac..191b9a987e5 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -56,7 +56,7 @@ add_library( eosio_chain ${CHAIN_WAVM_SOURCES} webassembly/wabt.cpp - webassembly/eos-vm.cpp + webassembly/eos-vm.cpp # get_config.cpp # diff --git a/libraries/eos-vm b/libraries/eos-vm index d09208662eb..78f7aa0bd75 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit d09208662eb5d313eb732dd00aafd733ba8fa9a3 +Subproject commit 78f7aa0bd75909c63f3ebcdc0ec3f012942c8523 From d370bc9285d64adc1ec402c59a840a388f7e9684 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 24 Sep 2019 09:55:16 -0400 Subject: [PATCH 85/88] Avoid compiling eos-vm when it isn't enabled. --- libraries/CMakeLists.txt | 2 ++ libraries/chain/CMakeLists.txt | 9 +++++++-- libraries/chain/include/eosio/chain/wasm_interface.hpp | 4 ++++ .../chain/include/eosio/chain/wasm_interface_private.hpp | 9 ++++++--- .../chain/include/eosio/chain/webassembly/eos-vm.hpp | 8 ++++++++ libraries/chain/wasm_interface.cpp | 8 +++++++- 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index ad7cc32f7dc..160c7cec10a 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -21,7 +21,9 @@ set(ENABLE_TESTS OFF CACHE BOOL "Build tests") set(ENABLE_ADDRESS_SANITIZER OFF CACHE BOOL "Use address sanitizer") set(ENABLE_UNDEFINED_BEHAVIOR_SANITIZER OFF CACHE BOOL "Use UB sanitizer") set(ENABLE_PROFILE OFF CACHE BOOL "Enable for profile builds") +if(eos-vm IN_LIST EOSIO_WASM_RUNTIMES OR eos-vm-jit IN_LIST EOSIO_WASM_RUNTIMES) add_subdirectory( eos-vm ) +endif() set(ENABLE_STATIC ON) set(CMAKE_MACOSX_RPATH OFF) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 191b9a987e5..ddd7b24654f 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -20,6 +20,11 @@ if("wavm" IN_LIST EOSIO_WASM_RUNTIMES) set(CHAIN_WAVM_SOURCES "webassembly/wavm.cpp") endif() +if("eos-vm" IN_LIST EOSIO_WASM_RUNTIMES OR "eos-vm-jit" IN_LIST EOSIO_WASM_RUNTIMES) + set(CHAIN_EOSVM_SOURCES "webassembly/eos-vm.cpp") + set(CHAIN_EOSVM_LIBRARIES eos-vm) +endif() + ## SORT .cpp by most likely to change / break compile add_library( eosio_chain merkle.cpp @@ -56,7 +61,7 @@ add_library( eosio_chain ${CHAIN_WAVM_SOURCES} webassembly/wabt.cpp - webassembly/eos-vm.cpp + ${CHAIN_EOSVM_SOURCES} # get_config.cpp # @@ -77,7 +82,7 @@ add_library( eosio_chain ) target_link_libraries( eosio_chain fc chainbase Logging IR WAST WASM Runtime - softfloat builtins wabt eos-vm + softfloat builtins wabt ${CHAIN_EOSVM_LIBRARIES} ) target_include_directories( eosio_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 2e59299b128..000c59dab31 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -3,7 +3,9 @@ #include #include #include +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) #include +#endif #include "Runtime/Linker.h" #include "Runtime/Runtime.h" @@ -88,8 +90,10 @@ namespace eosio { namespace chain { //validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints static void validate(const controller& control, const bytes& code); +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) //get the wasm_allocator used for the linear memory for wasm static vm::wasm_allocator* get_wasm_allocator(); +#endif //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 40dd98adae4..c57b1a05700 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -17,11 +17,12 @@ #include "WAST/WAST.h" #include "IR/Validate.h" +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) #include +#endif using namespace fc; using namespace eosio::chain::webassembly; -using namespace eosio::vm; using namespace IR; using namespace Runtime; @@ -69,10 +70,12 @@ namespace eosio { namespace chain { }); } - static wasm_allocator* get_wasm_allocator() { - static wasm_allocator walloc; +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) + static eosio::vm::wasm_allocator* get_wasm_allocator() { + static eosio::vm::wasm_allocator walloc; return &walloc; } +#endif std::vector parse_initial_memory(const Module& module) { std::vector mem_image; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index b62e77b90c4..f0b5fadc493 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -1,5 +1,7 @@ #pragma once +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) + #include #include #include @@ -139,3 +141,9 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { #define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) \ eosio::vm::registered_function _EOS_VM_INTRINSIC_NAME(__eos_vm_intrinsic_fn, __COUNTER__)(std::string(MOD), std::string(NAME)); + +#else + +#define _REGISTER_EOS_VM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) + +#endif diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index fe28ae91c6c..5abcbbaa41b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -25,19 +25,25 @@ #include #include +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) #include +#endif namespace eosio { namespace chain { using namespace webassembly; using namespace webassembly::common; wasm_interface::wasm_interface(vm_type vm, const chainbase::database& d) : my( new wasm_interface_impl(vm, d) ) { +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) (void)get_wasm_allocator(); +#endif } wasm_interface::~wasm_interface() {} - wasm_allocator* wasm_interface::get_wasm_allocator() { return wasm_interface_impl::get_wasm_allocator(); } +#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) + eosio::vm::wasm_allocator* wasm_interface::get_wasm_allocator() { return wasm_interface_impl::get_wasm_allocator(); } +#endif void wasm_interface::validate(const controller& control, const bytes& code) { Module module; From 7b49766ff6655326dfbb44b89b3b943570a03ade Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 24 Sep 2019 11:24:50 -0400 Subject: [PATCH 86/88] Bump timeout for unit tests again. --- .cicd/generate-pipeline.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cicd/generate-pipeline.sh b/.cicd/generate-pipeline.sh index 2711a9166ac..47305a94743 100755 --- a/.cicd/generate-pipeline.sh +++ b/.cicd/generate-pipeline.sh @@ -182,7 +182,7 @@ for ROUND in $(seq 1 $ROUNDS); do BUILDKITE_AGENT_ACCESS_TOKEN: agents: queue: "$BUILDKITE_AGENT_QUEUE" - timeout: ${TIMEOUT:-10} + timeout: ${TIMEOUT:-15} skip: \${SKIP_$(echo "$PLATFORM_JSON" | jq -r .PLATFORM_NAME_UPCASE)_$(echo "$PLATFORM_JSON" | jq -r .VERSION_MAJOR)$(echo "$PLATFORM_JSON" | jq -r .VERSION_MINOR)}${SKIP_UNIT_TESTS} EOF @@ -208,7 +208,7 @@ EOF - 'registry_1' - 'registry_2' pre-execute-sleep: 5 - timeout: ${TIMEOUT:-30} + timeout: ${TIMEOUT:-45} agents: "queue=mac-anka-node-fleet" skip: \${SKIP_$(echo "$PLATFORM_JSON" | jq -r .PLATFORM_NAME_UPCASE)_$(echo "$PLATFORM_JSON" | jq -r .VERSION_MAJOR)$(echo "$PLATFORM_JSON" | jq -r .VERSION_MINOR)}${SKIP_UNIT_TESTS} From 060e016114b0b1876b172bbc6cdb948a329766f6 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 25 Sep 2019 13:11:04 -0400 Subject: [PATCH 87/88] Address review feedback. --- libraries/chain/CMakeLists.txt | 2 +- libraries/chain/include/eosio/chain/webassembly/common.hpp | 3 --- libraries/chain/wasm_interface.cpp | 1 - libraries/eos-vm | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index ddd7b24654f..6a8bac0c995 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -87,7 +87,7 @@ target_link_libraries( eosio_chain fc chainbase Logging IR WAST WASM Runtime target_include_directories( eosio_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include" - "${CMAKE_CURRENT_SOURCE_DIR}/libraries/eos-vm/include" + "${CMAKE_CURRENT_SOURCE_DIR}/libraries/eos-vm/include" "${CMAKE_SOURCE_DIR}/libraries/wabt" "${CMAKE_BINARY_DIR}/libraries/wabt" ) diff --git a/libraries/chain/include/eosio/chain/webassembly/common.hpp b/libraries/chain/include/eosio/chain/webassembly/common.hpp index 6ec4b9b6a2b..7f441be8649 100644 --- a/libraries/chain/include/eosio/chain/webassembly/common.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/common.hpp @@ -58,8 +58,6 @@ namespace eosio { namespace chain { */ template struct array_ptr { - using type = T; - array_ptr() = default; explicit array_ptr (T * value) : value(value) {} typename std::add_lvalue_reference::type operator*() const { @@ -81,7 +79,6 @@ namespace eosio { namespace chain { * class to represent an in-wasm-memory char array that must be null terminated */ struct null_terminated_ptr { - null_terminated_ptr() = default; explicit null_terminated_ptr(char* value) : value(value) {} typename std::add_lvalue_reference::type operator*() const { diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 5abcbbaa41b..69382587f1b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1107,7 +1107,6 @@ class action_api : public context_aware_api { if( buffer_size == 0 ) return s; auto copy_size = std::min( static_cast(buffer_size), s ); - const char* d = context.get_action().data.data(); memcpy( (char*)memory.value, context.get_action().data.data(), copy_size ); return copy_size; diff --git a/libraries/eos-vm b/libraries/eos-vm index 78f7aa0bd75..d85ad1704b2 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 78f7aa0bd75909c63f3ebcdc0ec3f012942c8523 +Subproject commit d85ad1704b25b9a9833b65cdd6de5d94fa166de8 From 0adf5e7c5660e5033ab6b3a14e34921e0dcb0ab8 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 25 Sep 2019 16:10:09 -0400 Subject: [PATCH 88/88] untabify --- .../include/eosio/chain/webassembly/wabt.hpp | 16 ++++++++-------- .../include/eosio/chain/webassembly/wavm.hpp | 16 ++++++++-------- libraries/chain/wasm_interface.cpp | 10 +++++----- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp index 8bf978272f7..c4ae0907ed6 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wabt.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wabt.hpp @@ -703,32 +703,32 @@ using void_ret_wrapper_t = typename void_ret_wrapper::type; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; diff --git a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp index 509977e61be..fa6173730a2 100644 --- a/libraries/chain/include/eosio/chain/webassembly/wavm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/wavm.hpp @@ -700,32 +700,32 @@ struct intrinsic_function_invoker_wrapper; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; template struct intrinsic_function_invoker_wrapper { static_assert( !(std::is_pointer_v && alignof(std::remove_pointer_t>) != 1) && - !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), - "intrinsics should only return a reference or pointer with single byte alignment"); + !(std::is_lvalue_reference_v && alignof(std::remove_reference_t>) != 1), + "intrinsics should only return a reference or pointer with single byte alignment"); using type = intrinsic_function_invoker; }; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 69382587f1b..4ee48101a61 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -339,7 +339,7 @@ class softfloat_api : public context_aware_api { if ( f32_sign_bit(a) != f32_sign_bit(b) ) { return f32_sign_bit(a) ? af : bf; } - return ::f32_lt(a,b) ? af : bf; + return ::f32_lt(a,b) ? af : bf; } float _eosio_f32_max( float af, float bf ) { float32_t a = to_softfloat32(af); @@ -353,7 +353,7 @@ class softfloat_api : public context_aware_api { if ( f32_sign_bit(a) != f32_sign_bit(b) ) { return f32_sign_bit(a) ? bf : af; } - return ::f32_lt( a, b ) ? bf : af; + return ::f32_lt( a, b ) ? bf : af; } float _eosio_f32_copysign( float af, float bf ) { float32_t a = to_softfloat32(af); @@ -504,7 +504,7 @@ class softfloat_api : public context_aware_api { return bf; if (f64_sign_bit(a) != f64_sign_bit(b)) return f64_sign_bit(a) ? af : bf; - return ::f64_lt( a, b ) ? af : bf; + return ::f64_lt( a, b ) ? af : bf; } double _eosio_f64_max( double af, double bf ) { float64_t a = to_softfloat64(af); @@ -515,7 +515,7 @@ class softfloat_api : public context_aware_api { return bf; if (f64_sign_bit(a) != f64_sign_bit(b)) return f64_sign_bit(a) ? bf : af; - return ::f64_lt( a, b ) ? bf : af; + return ::f64_lt( a, b ) ? bf : af; } double _eosio_f64_copysign( double af, double bf ) { float64_t a = to_softfloat64(af); @@ -1003,7 +1003,7 @@ class authorization_api : public context_aware_api { } void require_auth2( account_name account, - permission_name permission) { + permission_name permission) { context.require_authorization( account, permission ); }