diff --git a/docs/01_nodeos/03_plugins/chain_plugin/index.md b/docs/01_nodeos/03_plugins/chain_plugin/index.md index 458ff88f6f..63ce8bed8f 100644 --- a/docs/01_nodeos/03_plugins/chain_plugin/index.md +++ b/docs/01_nodeos/03_plugins/chain_plugin/index.md @@ -179,7 +179,16 @@ Config Options for eosio::chain_plugin: code cache --eos-vm-oc-compile-threads arg (=1) Number of threads to use for EOS VM OC tier-up - --eos-vm-oc-enable Enable EOS VM OC tier-up runtime + --eos-vm-oc-enable arg (=auto) Enable EOS VM OC tier-up runtime + ('auto', 'all', 'none'). + 'auto' - EOS VM OC tier-up is enabled + for eosio.* accounts, read-only trxs, + and applying blocks. + 'all' - EOS VM OC tier-up is enabled + for all contract execution. + 'none' - EOS VM OC tier-up is + completely disabled. + --enable-account-queries arg (=0) enable queries to find accounts by various metadata. --max-nonprivileged-inline-action-size arg (=4096) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 10aa7e9066..b61ee77bbe 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -1094,4 +1094,19 @@ action_name apply_context::get_sender() const { return action_name(); } +// Context | OC? +//------------------------------------------------------------------------------- +// Building block | baseline, OC for eosio.* +// Applying block | OC unless a producer, OC for eosio.* including producers +// Speculative API trx | baseline, OC for eosio.* +// Speculative P2P trx | baseline, OC for eosio.* +// Compute trx | baseline, OC for eosio.* +// Read only trx | OC +bool apply_context::should_use_eos_vm_oc()const { + return receiver.prefix() == config::system_account_name // "eosio"_n, all cases use OC + || (is_applying_block() && !control.is_producer_node()) // validating/applying block + || trx_context.is_read_only(); +} + + } } /// eosio::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 3af8070e78..23fab9b182 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -239,6 +240,7 @@ struct controller_impl { controller::config conf; const chain_id_type chain_id; // read by thread_pool threads, value will not be changed bool replaying = false; + bool is_producer_node = false; // true if node is configured as a block producer db_read_mode read_mode = db_read_mode::HEAD; bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped std::optional subjective_cpu_leeway; @@ -249,14 +251,11 @@ struct controller_impl { deep_mind_handler* deep_mind_logger = nullptr; bool okay_to_print_integrity_hash_on_stop = false; - std::thread::id main_thread_id; thread_local static platform_timer timer; // a copy for main thread and each read-only thread #if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) thread_local static vm::wasm_allocator wasm_alloc; // a copy for main thread and each read-only thread #endif - wasm_interface wasmif; // used by main thread and all threads for EOSVMOC - std::mutex threaded_wasmifs_mtx; - std::unordered_map> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit + wasm_interface_collection wasm_if_collect; app_window_type app_window = app_window_type::write; typedef pair handler_key; @@ -315,8 +314,7 @@ struct controller_impl { chain_id( chain_id ), read_mode( cfg.read_mode ), thread_pool(), - main_thread_id( std::this_thread::get_id() ), - wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) + wasm_if_collect( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) { fork_db.open( [this]( block_timestamp_type timestamp, const flat_set& cur_features, @@ -342,12 +340,7 @@ struct controller_impl { set_activation_handler(); self.irreversible_block.connect([this](const block_state_ptr& bsp) { - // producer_plugin has already asserted irreversible_block signal is - // called in write window - wasmif.current_lib(bsp->block_num); - for (auto& w: threaded_wasmifs) { - w.second->current_lib(bsp->block_num); - } + wasm_if_collect.current_lib(bsp->block_num); }); @@ -2685,31 +2678,6 @@ struct controller_impl { return (blog.first_block_num() != 0) ? blog.first_block_num() : fork_db.root()->block_num; } -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - bool is_eos_vm_oc_enabled() const { - return ( conf.eosvmoc_tierup || conf.wasm_runtime == wasm_interface::vm_type::eos_vm_oc ); - } -#endif - - // only called from non-main threads (read-only trx execution threads) - // when producer_plugin starts them - void init_thread_local_data() { - EOS_ASSERT( !is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread"); -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if ( is_eos_vm_oc_enabled() ) - // EOSVMOC needs further initialization of its thread local data - wasmif.init_thread_local_data(); - else -#endif - { - std::lock_guard g(threaded_wasmifs_mtx); - // Non-EOSVMOC needs a wasmif per thread - threaded_wasmifs[std::this_thread::get_id()] = std::make_unique( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty()); - } - } - - bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); }; - void set_to_write_window() { app_window = app_window_type::write; } @@ -2720,25 +2688,22 @@ struct controller_impl { return app_window == app_window_type::write; } - wasm_interface& get_wasm_interface() { - if ( is_on_main_thread() #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - || is_eos_vm_oc_enabled() + bool is_eos_vm_oc_enabled() const { + return wasm_if_collect.is_eos_vm_oc_enabled(); + } #endif - ) - return wasmif; - else - return *threaded_wasmifs[std::this_thread::get_id()]; + + void init_thread_local_data() { + wasm_if_collect.init_thread_local_data(db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty()); + } + + wasm_interface& get_wasm_interface() { + return wasm_if_collect.get_wasm_interface(); } void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) { - // The caller of this function apply_eosio_setcode has already asserted that - // the transaction is not a read-only trx, which implies we are - // in write window. Safe to call threaded_wasmifs's code_block_num_last_used - wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); - for (auto& w: threaded_wasmifs) { - w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); - } + wasm_if_collect.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); } block_state_ptr fork_db_head() const; @@ -3615,7 +3580,6 @@ vm::wasm_allocator& controller::get_wasm_allocator() { return my->wasm_alloc; } #endif - #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED bool controller::is_eos_vm_oc_enabled() const { return my->is_eos_vm_oc_enabled(); @@ -3723,6 +3687,14 @@ void controller::replace_account_keys( name account, name permission, const publ rlm.verify_account_ram_usage(account); } +void controller::set_producer_node(bool is_producer_node) { + my->is_producer_node = is_producer_node; +} + +bool controller::is_producer_node()const { + return my->is_producer_node; +} + void controller::set_db_read_only_mode() { mutable_db().set_read_only_mode(); } diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 78a4fa0e0a..090531bfcb 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -598,6 +598,9 @@ class apply_context { action_name get_sender() const; + bool is_applying_block() const { return trx_context.explicit_billed_cpu_time; } + bool should_use_eos_vm_oc()const; + /// Fields: public: diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 97bc488084..1ffcc4e8b4 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -90,7 +90,7 @@ namespace eosio { namespace chain { wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime; eosvmoc::config eosvmoc_config; - bool eosvmoc_tierup = false; + wasm_interface::vm_oc_enable eosvmoc_tierup = wasm_interface::vm_oc_enable::oc_auto; db_read_mode read_mode = db_read_mode::HEAD; validation_mode block_validation_mode = validation_mode::FULL; @@ -321,6 +321,8 @@ namespace eosio { namespace chain { #if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) vm::wasm_allocator& get_wasm_allocator(); +#endif +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED bool is_eos_vm_oc_enabled() const; #endif @@ -355,6 +357,9 @@ namespace eosio { namespace chain { void replace_producer_keys( const public_key_type& key ); void replace_account_keys( name account, name permission, const public_key_type& key ); + void set_producer_node(bool is_producer_node); + bool is_producer_node()const; + void set_db_read_only_mode(); void unset_db_read_only_mode(); void init_thread_local_data(); diff --git a/libraries/chain/include/eosio/chain/name.hpp b/libraries/chain/include/eosio/chain/name.hpp index 20bdc51549..c1a5b423f0 100644 --- a/libraries/chain/include/eosio/chain/name.hpp +++ b/libraries/chain/include/eosio/chain/name.hpp @@ -75,6 +75,41 @@ namespace eosio::chain { friend constexpr bool operator != ( const name& a, uint64_t b ) { return a.value != b; } constexpr explicit operator bool()const { return value != 0; } + + /** + * Returns the prefix. + * for exmaple: + * "eosio.any" -> "eosio" + * "eosio" -> "eosio" + */ + constexpr name prefix() const { + uint64_t result = value; + bool not_dot_character_seen = false; + uint64_t mask = 0xFull; + + // Get characters one-by-one in name in order from right to left + for (int32_t offset = 0; offset <= 59;) { + auto c = (value >> offset) & mask; + + if (!c) { // if this character is a dot + if (not_dot_character_seen) { // we found the rightmost dot character + result = (value >> offset) << offset; + break; + } + } else { + not_dot_character_seen = true; + } + + if (offset == 0) { + offset += 4; + mask = 0x1Full; + } else { + offset += 5; + } + } + + return name{ result }; + } }; // Each char of the string is encoded into 5-bit chunk and left-shifted diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index fb2abc65b7..f5bc4309a3 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -184,7 +184,6 @@ namespace eosio { namespace chain { speculative_executed_adjusted_max_transaction_time // prev_billed_cpu_time_us > 0 }; tx_cpu_usage_exceeded_reason tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit; - fc::microseconds tx_cpu_usage_amount; }; } } diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 8d832a17ff..4d3964dda0 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -40,7 +40,13 @@ namespace eosio { namespace chain { } } - wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile); + enum class vm_oc_enable { + oc_auto, + oc_all, + oc_none + }; + + wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile); ~wasm_interface(); // initialize exec per thread @@ -70,13 +76,22 @@ namespace eosio { namespace chain { const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, apply_context& context)> substitute_apply; private: unique_ptr my; - vm_type vm; }; } } // eosio::chain namespace eosio{ namespace chain { std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime); + inline std::ostream& operator<<(std::ostream& os, wasm_interface::vm_oc_enable t) { + if (t == wasm_interface::vm_oc_enable::oc_auto) { + os << "auto"; + } else if (t == wasm_interface::vm_oc_enable::oc_all) { + os << "all"; + } else if (t == wasm_interface::vm_oc_enable::oc_none) { + os << "none"; + } + return os; + } }} FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (eos_vm)(eos_vm_jit)(eos_vm_oc) ) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp b/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp new file mode 100644 index 0000000000..8245406290 --- /dev/null +++ b/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp @@ -0,0 +1,85 @@ +#pragma once +#include + +namespace eosio::chain { + + /** + * @class wasm_interface_collection manages the active wasm_interface to use for execution. + */ + class wasm_interface_collection { + public: + wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, + const chainbase::database& d, const std::filesystem::path& data_dir, + const eosvmoc::config& eosvmoc_config, bool profile) + : main_thread_id(std::this_thread::get_id()) + , wasm_runtime(vm) + , eosvmoc_tierup(eosvmoc_tierup) + , wasmif(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) + {} + + wasm_interface& get_wasm_interface() { + if (is_on_main_thread() +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + || is_eos_vm_oc_enabled() +#endif + ) + return wasmif; + return *threaded_wasmifs[std::this_thread::get_id()]; + } + + // update current lib of all wasm interfaces + void current_lib(const uint32_t lib) { + // producer_plugin has already asserted irreversible_block signal is called in write window + wasmif.current_lib(lib); + for (auto& w: threaded_wasmifs) { + w.second->current_lib(lib); + } + } + + // only called from non-main threads (read-only trx execution threads) when producer_plugin starts them + void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, + const eosvmoc::config& eosvmoc_config, bool profile) { + EOS_ASSERT(!is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread"); +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + if (is_eos_vm_oc_enabled()) { + // EOSVMOC needs further initialization of its thread local data + wasmif.init_thread_local_data(); + } else +#endif + { + std::lock_guard g(threaded_wasmifs_mtx); + // Non-EOSVMOC needs a wasmif per thread + threaded_wasmifs[std::this_thread::get_id()] = std::make_unique(wasm_runtime, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile); + } + } + +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + bool is_eos_vm_oc_enabled() const { + return ((eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) || wasm_runtime == wasm_interface::vm_type::eos_vm_oc); + } +#endif + + void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) { + // The caller of this function apply_eosio_setcode has already asserted that + // the transaction is not a read-only trx, which implies we are + // in write window. Safe to call threaded_wasmifs's code_block_num_last_used + wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); + for (auto& w: threaded_wasmifs) { + w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); + } + } + + private: + bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); }; + + private: + const std::thread::id main_thread_id; + const wasm_interface::vm_type wasm_runtime; + const wasm_interface::vm_oc_enable eosvmoc_tierup; + + wasm_interface wasmif; // used by main thread and all threads for EOSVMOC + std::mutex threaded_wasmifs_mtx; + std::unordered_map> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit + }; + +} // eosio::chain diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 825861ac58..1181976285 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -41,7 +41,6 @@ namespace eosio { namespace chain { uint8_t vm_version = 0; }; struct by_hash; - struct by_first_block_num; struct by_last_block_num; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED @@ -65,7 +64,12 @@ namespace eosio { namespace chain { }; #endif - wasm_interface_impl(wasm_interface::vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) : db(d), wasm_runtime_time(vm) { + wasm_interface_impl(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, const chainbase::database& d, + const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) + : db(d) + , wasm_runtime_time(vm) + , eosvmoc_tierup(eosvmoc_tierup) + { #ifdef EOSIO_EOS_VM_RUNTIME_ENABLED if(vm == wasm_interface::vm_type::eos_vm) runtime_interface = std::make_unique>(); @@ -86,7 +90,7 @@ namespace eosio { namespace chain { EOS_THROW(wasm_exception, "${r} wasm runtime not supported on this platform and/or configuration", ("r", vm)); #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if(eosvmoc_tierup) { + if(eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) { EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated"); eosvmoc.emplace(data_dir, eosvmoc_config, d); } @@ -178,6 +182,7 @@ namespace eosio { namespace chain { const chainbase::database& db; const wasm_interface::vm_type wasm_runtime_time; + const wasm_interface::vm_oc_enable eosvmoc_tierup; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED std::optional eosvmoc; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index c66a514eec..33bb863dbc 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -32,8 +32,8 @@ namespace eosio { namespace chain { - wasm_interface::wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) - : my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ), vm( vm ) {} + wasm_interface::wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) + : my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ) {} wasm_interface::~wasm_interface() {} @@ -41,7 +41,7 @@ namespace eosio { namespace chain { void wasm_interface::init_thread_local_data() { if (my->eosvmoc) my->eosvmoc->init_thread_local_data(); - else if (vm == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface) + else if (my->wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface) my->runtime_interface->init_thread_local_data(); } #endif @@ -90,7 +90,7 @@ namespace eosio { namespace chain { if(substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context)) return; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if(my->eosvmoc) { + if(my->eosvmoc && (my->eosvmoc_tierup == wasm_interface::vm_oc_enable::oc_all || context.should_use_eos_vm_oc())) { const chain::eosvmoc::code_descriptor* cd = nullptr; chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary; try { @@ -105,6 +105,8 @@ namespace eosio { namespace chain { once_is_enough = true; } if(cd) { + if (!context.is_applying_block()) // read_only_trx_test.py looks for this log statement + tlog("${a} speculatively executing ${h} with eos vm oc", ("a", context.get_receiver())("h", code_hash)); my->eosvmoc->exec->execute(*cd, my->eosvmoc->mem, context); return; } diff --git a/libraries/state_history/include/eosio/state_history/log.hpp b/libraries/state_history/include/eosio/state_history/log.hpp index a190c393ac..567294c2a2 100644 --- a/libraries/state_history/include/eosio/state_history/log.hpp +++ b/libraries/state_history/include/eosio/state_history/log.hpp @@ -460,7 +460,7 @@ class state_history_log { return get_block_id_i(block_num); } -#ifdef BOOST_TEST_MODULE +#ifdef BOOST_TEST fc::cfile& get_log_file() { return log;} #endif diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index c07a1bda71..fef992a2f5 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -404,6 +404,9 @@ namespace eosio { namespace testing { cfg.contracts_console = true; cfg.eosvmoc_config.cache_size = 1024*1024*8; + // don't use auto tier up for tests, since the point is to test diff vms + cfg.eosvmoc_tierup = chain::wasm_interface::vm_oc_enable::oc_none; + for(int i = 0; i < boost::unit_test::framework::master_test_suite().argc; ++i) { if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--eos-vm")) cfg.wasm_runtime = chain::wasm_interface::vm_type::eos_vm; diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 1454a626ae..973b127218 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -114,6 +114,32 @@ void validate(boost::any& v, } } +void validate(boost::any& v, + const std::vector& values, + wasm_interface::vm_oc_enable* /* target_type */, + int) +{ + using namespace boost::program_options; + + // Make sure no previous assignment to 'v' was made. + validators::check_first_occurrence(v); + + // Extract the first string from 'values'. If there is more than + // one string, it's an error, and exception will be thrown. + std::string s = validators::get_single_string(values); + boost::algorithm::to_lower(s); + + if (s == "auto") { + v = boost::any(wasm_interface::vm_oc_enable::oc_auto); + } else if (s == "all" || s == "true" || s == "on" || s == "yes" || s == "1") { + v = boost::any(wasm_interface::vm_oc_enable::oc_all); + } else if (s == "none" || s == "false" || s == "off" || s == "no" || s == "0") { + v = boost::any(wasm_interface::vm_oc_enable::oc_none); + } else { + throw validation_error(validation_error::invalid_option_value); + } +} + } // namespace chain using namespace eosio; @@ -203,6 +229,7 @@ chain_plugin::chain_plugin() app().register_config_type(); app().register_config_type(); app().register_config_type(); + app().register_config_type(); } chain_plugin::~chain_plugin() = default; @@ -227,7 +254,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip #ifdef EOSIO_EOS_VM_OC_DEVELOPER wasm_runtime_opt += delim + "\"eos-vm-oc\""; - wasm_runtime_desc += "\"eos-vm-oc\" : Unsupported. Instead, use one of the other runtimes along with the option enable-eos-vm-oc.\n"; + wasm_runtime_desc += "\"eos-vm-oc\" : Unsupported. Instead, use one of the other runtimes along with the option eos-vm-oc-enable.\n"; #endif wasm_runtime_opt += ")\n" + wasm_runtime_desc; @@ -334,7 +361,11 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip EOS_ASSERT(false, plugin_exception, ""); } }), "Number of threads to use for EOS VM OC tier-up") - ("eos-vm-oc-enable", bpo::bool_switch(), "Enable EOS VM OC tier-up runtime") + ("eos-vm-oc-enable", bpo::value()->default_value(chain::wasm_interface::vm_oc_enable::oc_auto), + "Enable EOS VM OC tier-up runtime ('auto', 'all', 'none').\n" + "'auto' - EOS VM OC tier-up is enabled for eosio.* accounts, read-only trxs, and except on producers applying blocks.\n" + "'all' - EOS VM OC tier-up is enabled for all contract execution.\n" + "'none' - EOS VM OC tier-up is completely disabled.\n") #endif ("enable-account-queries", bpo::value()->default_value(false), "enable queries to find accounts by various metadata.") ("max-nonprivileged-inline-action-size", bpo::value()->default_value(config::default_max_nonprivileged_inline_action_size), "maximum allowed size (in bytes) of an inline action for a nonprivileged account") @@ -909,8 +940,7 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { chain_config->eosvmoc_config.cache_size = options.at( "eos-vm-oc-cache-size-mb" ).as() * 1024u * 1024u; if( options.count("eos-vm-oc-compile-threads") ) chain_config->eosvmoc_config.threads = options.at("eos-vm-oc-compile-threads").as(); - if( options["eos-vm-oc-enable"].as() ) - chain_config->eosvmoc_tierup = true; + chain_config->eosvmoc_tierup = options["eos-vm-oc-enable"].as(); #endif account_queries_enabled = options.at("enable-account-queries").as(); diff --git a/plugins/chain_plugin/test/CMakeLists.txt b/plugins/chain_plugin/test/CMakeLists.txt index ec397e710d..81473d42e9 100644 --- a/plugins/chain_plugin/test/CMakeLists.txt +++ b/plugins/chain_plugin/test/CMakeLists.txt @@ -5,5 +5,5 @@ add_executable( test_chain_plugin plugin_config_test.cpp main.cpp ) -target_link_libraries( test_chain_plugin chain_plugin eosio_testing) +target_link_libraries( test_chain_plugin chain_plugin eosio_testing eosio_chain_wrap ) add_test(NAME test_chain_plugin COMMAND plugins/chain_plugin/test/test_chain_plugin WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/plugins/chain_plugin/test/plugin_config_test.cpp b/plugins/chain_plugin/test/plugin_config_test.cpp index ce308cc2e4..e43b0bfbd3 100644 --- a/plugins/chain_plugin/test/plugin_config_test.cpp +++ b/plugins/chain_plugin/test/plugin_config_test.cpp @@ -5,8 +5,8 @@ #include BOOST_AUTO_TEST_CASE(chain_plugin_default_tests) { - appbase::scoped_app app; fc::temp_directory tmp; + appbase::scoped_app app; auto tmp_path = tmp.path().string(); std::array args = { diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index b1bfc8f5d3..4ad6fd9730 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1033,7 +1033,9 @@ void producer_plugin_impl::plugin_initialize(const boost::program_options::varia _options = &options; LOAD_VALUE_SET(options, "producer-name", _producers) - chain::controller& chain = chain_plug->chain(); + chain::controller& chain = chain_plug->chain(); + + chain.set_producer_node(!_producers.empty()); if (options.count("signature-provider")) { const std::vector key_spec_pairs = options["signature-provider"].as>(); diff --git a/plugins/producer_plugin/test/CMakeLists.txt b/plugins/producer_plugin/test/CMakeLists.txt index 2eee8c8b8f..043d56791c 100644 --- a/plugins/producer_plugin/test/CMakeLists.txt +++ b/plugins/producer_plugin/test/CMakeLists.txt @@ -5,5 +5,5 @@ add_executable( test_producer_plugin test_block_timing_util.cpp main.cpp ) -target_link_libraries( test_producer_plugin producer_plugin eosio_testing ) +target_link_libraries( test_producer_plugin producer_plugin eosio_testing eosio_chain_wrap ) add_test(NAME test_producer_plugin COMMAND plugins/producer_plugin/test/test_producer_plugin WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/plugins/producer_plugin/test/test_read_only_trx.cpp b/plugins/producer_plugin/test/test_read_only_trx.cpp index 188bf7366c..19c25d0d60 100644 --- a/plugins/producer_plugin/test/test_read_only_trx.cpp +++ b/plugins/producer_plugin/test/test_read_only_trx.cpp @@ -52,8 +52,8 @@ BOOST_AUTO_TEST_SUITE(read_only_trxs) enum class app_init_status { failed, succeeded }; void test_configs_common(std::vector& specific_args, app_init_status expected_status) { - appbase::scoped_app app; fc::temp_directory temp; + appbase::scoped_app app; auto temp_dir_str = temp.path().string(); fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); @@ -92,8 +92,8 @@ BOOST_AUTO_TEST_CASE(not_check_configs_if_no_read_only_threads) { void test_trxs_common(std::vector& specific_args) { using namespace std::chrono_literals; - appbase::scoped_app app; fc::temp_directory temp; + appbase::scoped_app app; auto temp_dir_str = temp.path().string(); producer_plugin::set_test_mode(true); @@ -187,10 +187,22 @@ BOOST_AUTO_TEST_CASE(with_1_read_only_threads) { test_trxs_common(specific_args); } +// test read-only trxs on 3 threads (with --read-only-threads) +BOOST_AUTO_TEST_CASE(with_3_read_only_threads) { + std::vector specific_args = { "-p", "eosio", "-e", + "--read-only-threads=3", + "--max-transaction-time=10", + "--abi-serializer-max-time-ms=999", + "--read-only-write-window-time-us=100000", + "--read-only-read-window-time-us=40000" }; + test_trxs_common(specific_args); +} + // test read-only trxs on 8 separate threads (with --read-only-threads) BOOST_AUTO_TEST_CASE(with_8_read_only_threads) { std::vector specific_args = { "-p", "eosio", "-e", "--read-only-threads=8", + "--eos-vm-oc-enable=none", "--max-transaction-time=10", "--abi-serializer-max-time-ms=999", "--read-only-write-window-time-us=100000", diff --git a/plugins/producer_plugin/test/test_trx_full.cpp b/plugins/producer_plugin/test/test_trx_full.cpp index b8c313bd29..34ddcc6ea9 100644 --- a/plugins/producer_plugin/test/test_trx_full.cpp +++ b/plugins/producer_plugin/test/test_trx_full.cpp @@ -99,9 +99,9 @@ BOOST_AUTO_TEST_SUITE(ordered_trxs_full) // Test verifies that transactions are processed, reported to caller, and not lost // even when blocks are aborted and some transactions fail. BOOST_AUTO_TEST_CASE(producer) { + fc::temp_directory temp; appbase::scoped_app app; - fc::temp_directory temp; auto temp_dir_str = temp.path().string(); { diff --git a/plugins/state_history_plugin/tests/CMakeLists.txt b/plugins/state_history_plugin/tests/CMakeLists.txt index 3c018c7bc6..c01c62df61 100644 --- a/plugins/state_history_plugin/tests/CMakeLists.txt +++ b/plugins/state_history_plugin/tests/CMakeLists.txt @@ -1,5 +1,5 @@ -add_executable( test_state_history session_test.cpp plugin_config_test.cpp) -target_link_libraries(test_state_history state_history_plugin Boost::unit_test_framework) +add_executable( test_state_history main.cpp session_test.cpp plugin_config_test.cpp) +target_link_libraries(test_state_history state_history_plugin eosio_testing eosio_chain_wrap) target_include_directories( test_state_history PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include" ) add_test(test_state_history test_state_history) \ No newline at end of file diff --git a/plugins/state_history_plugin/tests/main.cpp b/plugins/state_history_plugin/tests/main.cpp new file mode 100644 index 0000000000..e618f36999 --- /dev/null +++ b/plugins/state_history_plugin/tests/main.cpp @@ -0,0 +1,2 @@ +#define BOOST_TEST_MODULE state_history_plugin +#include \ No newline at end of file diff --git a/plugins/state_history_plugin/tests/session_test.cpp b/plugins/state_history_plugin/tests/session_test.cpp index 7d843ba8e1..68a0bad39e 100644 --- a/plugins/state_history_plugin/tests/session_test.cpp +++ b/plugins/state_history_plugin/tests/session_test.cpp @@ -1,5 +1,3 @@ - -#define BOOST_TEST_MODULE example #include #include diff --git a/programs/leap-util/actions/snapshot.cpp b/programs/leap-util/actions/snapshot.cpp index cfa105a7ce..7e98040f7a 100644 --- a/programs/leap-util/actions/snapshot.cpp +++ b/programs/leap-util/actions/snapshot.cpp @@ -79,6 +79,7 @@ int snapshot_actions::run_subcommand() { cfg.state_dir = state_dir; cfg.state_size = opt->db_size * 1024 * 1024; cfg.state_guard_size = opt->guard_size * 1024 * 1024; + cfg.eosvmoc_tierup = wasm_interface::vm_oc_enable::oc_none; // wasm not used, no use to fire up oc protocol_feature_set pfs = initialize_protocol_features( std::filesystem::path("protocol_features"), false ); try { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 40c32c9a7a..0dbeb89358 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,7 @@ list(REMOVE_ITEM UNIT_TESTS ship_client.cpp) list(REMOVE_ITEM UNIT_TESTS ship_streamer.cpp) add_executable( plugin_test ${UNIT_TESTS} ) -target_link_libraries( plugin_test eosio_testing eosio_chain chainbase chain_plugin producer_plugin wallet_plugin fc state_history ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( plugin_test eosio_testing eosio_chain_wrap chainbase chain_plugin producer_plugin wallet_plugin fc state_history ${PLATFORM_SPECIFIC_LIBS} ) target_include_directories( plugin_test PUBLIC ${CMAKE_SOURCE_DIR}/plugins/net_plugin/include @@ -132,8 +132,10 @@ add_test(NAME read-only-trx-basic-test COMMAND tests/read_only_trx_test.py -v -p set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -v -p 2 -n 3 --read-only-threads 6 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST read-only-trx-parallel-test PROPERTY LABELS nonparallelizable_tests) -add_test(NAME read-only-trx-parallel-eos-vm-oc-test COMMAND tests/read_only_trx_test.py -v -p 2 -n 3 --eos-vm-oc-enable --read-only-threads 6 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME read-only-trx-parallel-eos-vm-oc-test COMMAND tests/read_only_trx_test.py -v -p 2 -n 3 --eos-vm-oc-enable all --read-only-threads 6 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST read-only-trx-parallel-eos-vm-oc-test PROPERTY LABELS nonparallelizable_tests) +add_test(NAME read-only-trx-parallel-no-oc-test COMMAND tests/read_only_trx_test.py -v -p 2 -n 3 --eos-vm-oc-enable none --read-only-threads 6 --num-test-runs 2 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST read-only-trx-parallel-no-oc-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME subjective_billing_test COMMAND tests/subjective_billing_test.py -v -p 2 -n 4 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST subjective_billing_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME get_account_test COMMAND tests/get_account_test.py -v -p 2 -n 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/TestHarness/Node.py b/tests/TestHarness/Node.py index 1487972315..76b7aa868c 100644 --- a/tests/TestHarness/Node.py +++ b/tests/TestHarness/Node.py @@ -537,6 +537,16 @@ def findStderrFiles(path): files.sort() return files + def findInLog(self, searchStr): + dataDir=Utils.getNodeDataDir(self.nodeId) + files=Node.findStderrFiles(dataDir) + for file in files: + with open(file, 'r') as f: + for line in f: + if searchStr in line: + return True + return False + def analyzeProduction(self, specificBlockNum=None, thresholdMs=500): dataDir=Utils.getNodeDataDir(self.nodeId) files=Node.findStderrFiles(dataDir) diff --git a/tests/performance_tests/genesis.json b/tests/performance_tests/genesis.json index 57474c902d..a215407af4 100644 --- a/tests/performance_tests/genesis.json +++ b/tests/performance_tests/genesis.json @@ -11,7 +11,7 @@ "context_free_discount_net_usage_den": 100, "max_block_cpu_usage": 500000, "target_block_cpu_usage_pct": 500, - "max_transaction_cpu_usage": 90000, + "max_transaction_cpu_usage": 150000, "min_transaction_cpu_usage": 0, "max_transaction_lifetime": 3600, "deferred_trx_expiration_window": 600, diff --git a/tests/performance_tests/performance_test_basic.py b/tests/performance_tests/performance_test_basic.py index fd82bc6d15..a22b373c04 100755 --- a/tests/performance_tests/performance_test_basic.py +++ b/tests/performance_tests/performance_test_basic.py @@ -131,7 +131,7 @@ def configureValidationNodes(): if not self.prodsEnableTraceApi: validationNodeSpecificNodeosStr += "--plugin eosio::trace_api_plugin " if self.nonProdsEosVmOcEnable: - validationNodeSpecificNodeosStr += "--eos-vm-oc-enable " + validationNodeSpecificNodeosStr += "--eos-vm-oc-enable all " if validationNodeSpecificNodeosStr: self.specificExtraNodeosArgs.update({f"{nodeId}" : validationNodeSpecificNodeosStr for nodeId in self._validationNodeIds}) diff --git a/tests/read_only_trx_test.py b/tests/read_only_trx_test.py index 2769b919b2..3ee600fe8c 100755 --- a/tests/read_only_trx_test.py +++ b/tests/read_only_trx_test.py @@ -22,7 +22,7 @@ appArgs=AppArgs() appArgs.add(flag="--read-only-threads", type=int, help="number of read-only threads", default=0) appArgs.add(flag="--num-test-runs", type=int, help="number of times to run the tests", default=1) -appArgs.add_bool(flag="--eos-vm-oc-enable", help="enable eos-vm-oc") +appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto") appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="eos-vm-jit") args=TestHelper.parse_args({"-p","-n","-d","-s","--nodes-file","--seed" @@ -47,9 +47,12 @@ Utils.Debug=debug testSuccessful=False errorInThread=False +noOC = args.eos_vm_oc_enable == "none" +allOC = args.eos_vm_oc_enable == "all" random.seed(seed) # Use a fixed seed for repeatability. -cluster=Cluster(unshared=args.unshared, keepRunning=True if nodesFile is not None else args.leave_running, keepLogs=args.keep_logs) +# all debuglevel so that "executing ${h} with eos vm oc" is logged +cluster=Cluster(loggingLevel="all", unshared=args.unshared, keepRunning=True if nodesFile is not None else args.leave_running, keepLogs=args.keep_logs) walletMgr=WalletMgr(True) EOSIO_ACCT_PRIVATE_DEFAULT_KEY = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" @@ -61,6 +64,15 @@ userAccountName = "user" payloadlessAccountName = "payloadless" +def getCodeHash(node, account): + # Example get code result: code hash: 67d0598c72e2521a1d588161dad20bbe9f8547beb5ce6d14f3abd550ab27d3dc + cmd = f"get code {account}" + codeHash = node.processCleosCmd(cmd, cmd, silentErrors=False, returnType=ReturnType.raw) + if codeHash is None: errorExit(f"Unable to get code {account} from node {node.nodeId}") + else: codeHash = codeHash.split(' ')[2].strip() + if Utils.Debug: Utils.Print(f"{account} code hash: {codeHash}") + return codeHash + def startCluster(): global total_nodes global producerNode @@ -91,7 +103,8 @@ def startCluster(): specificExtraNodeosArgs[pnodes]+=" --read-only-threads " specificExtraNodeosArgs[pnodes]+=str(args.read_only_threads) if args.eos_vm_oc_enable: - specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-enable" + specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-enable " + specificExtraNodeosArgs[pnodes]+=args.eos_vm_oc_enable if args.wasm_runtime: specificExtraNodeosArgs[pnodes]+=" --wasm-runtime " specificExtraNodeosArgs[pnodes]+=args.wasm_runtime @@ -107,6 +120,12 @@ def startCluster(): producerNode = cluster.getNode() apiNode = cluster.nodes[-1] + eosioCodeHash = getCodeHash(producerNode, "eosio.token") + # eosio.* should be using oc unless oc tierup disabled + Utils.Print(f"search: executing {eosioCodeHash} with eos vm oc") + found = producerNode.findInLog(f"executing {eosioCodeHash} with eos vm oc") + assert( found or (noOC and not found) ) + def deployTestContracts(): Utils.Print("create test accounts") testAccount = Account(testAccountName) @@ -243,6 +262,10 @@ def basicTests(): assert(results[0]) apiNode.waitForTransactionInBlock(results[1]['transaction_id']) + testAccountCodeHash = getCodeHash(producerNode, testAccountName) + found = producerNode.findInLog(f"executing {testAccountCodeHash} with eos vm oc") + assert( (allOC and found) or not found ) + # verify the return value (age) from read-only is the same as created. Print("Send a read-only Get transaction to verify previous Insert") results = sendTransaction(testAccountName, 'getage', {"user": userAccountName}, opts='--read') diff --git a/unittests/misc_tests.cpp b/unittests/misc_tests.cpp index 64427568a5..4f61553040 100644 --- a/unittests/misc_tests.cpp +++ b/unittests/misc_tests.cpp @@ -139,6 +139,27 @@ BOOST_AUTO_TEST_CASE(name_suffix_tests) BOOST_CHECK_EQUAL( name{name_suffix("abcdefhij.123"_n)}, name{"123"_n} ); } +BOOST_AUTO_TEST_CASE(name_prefix_tests) +{ + BOOST_CHECK_EQUAL("e"_n.prefix(), "e"_n); + BOOST_CHECK_EQUAL(""_n.prefix(), ""_n); + BOOST_CHECK_EQUAL("abcdefghijklm"_n.prefix(), "abcdefghijklm"_n); + BOOST_CHECK_EQUAL("abcdefghijkl"_n.prefix(), "abcdefghijkl"_n); + BOOST_CHECK_EQUAL("abc.xyz"_n.prefix(), "abc"_n); + BOOST_CHECK_EQUAL("abc.xyz.qrt"_n.prefix(), "abc.xyz"_n); + BOOST_CHECK_EQUAL("."_n.prefix(), ""_n); + + BOOST_CHECK_EQUAL("eosio.any"_n.prefix(), "eosio"_n); + BOOST_CHECK_EQUAL("eosio"_n.prefix(), "eosio"_n); + BOOST_CHECK_EQUAL("eosio"_n.prefix(), config::system_account_name); + BOOST_CHECK_EQUAL("eosio."_n.prefix(), "eosio"_n); + BOOST_CHECK_EQUAL("eosio.evm"_n.prefix(), "eosio"_n); + BOOST_CHECK_EQUAL(".eosio"_n.prefix(), ""_n); + BOOST_CHECK_NE("eosi"_n.prefix(), "eosio"_n); + BOOST_CHECK_NE("eosioeosio"_n.prefix(), "eosio"_n); + BOOST_CHECK_NE("eosioe"_n.prefix(), "eosio"_n); +} + /// Test processing of unbalanced strings BOOST_AUTO_TEST_CASE(json_from_string_test) {