Merge branch 'main' of github.com:AntelopeIO/leap into gh-1189-test

This commit is contained in:
greg7mdp
2023-07-03 11:12:03 -04:00
45 changed files with 741 additions and 476 deletions
@@ -0,0 +1,21 @@
name: "Performance Harness Backwards Compatibility"
on:
workflow_dispatch:
permissions:
packages: read
contents: read
defaults:
run:
shell: bash
jobs:
tmp:
name: Stub
runs-on: ubuntu-latest
steps:
- name: Workflow Stub
run: |
echo "Workflow Stub"
@@ -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)
@@ -192,13 +201,17 @@ Config Options for eosio::chain_plugin:
feature. Setting above 0 enables this
feature.
--transaction-retry-interval-sec arg (=20)
How often, in seconds, to resend an
incoming transaction to network if not
How often, in seconds, to resend an
incoming transaction to network if not
seen in a block.
Needs to be at least twice as large as
p2p-dedup-cache-expire-time-sec.
--transaction-retry-max-expiration-sec arg (=120)
Maximum allowed transaction expiration
for retry transactions, will retry
Maximum allowed transaction expiration
for retry transactions, will retry
transactions up to this value.
Should be larger than
transaction-retry-interval-sec.
--transaction-finality-status-max-storage-size-gb arg
Maximum size (in GiB) allowed to be
allocated for the Transaction Finality
+15
View File
@@ -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
+21 -25
View File
@@ -462,8 +462,11 @@ namespace eosio { namespace chain {
inline static uint32_t default_initial_version = block_log::max_supported_version;
std::mutex mtx;
signed_block_ptr head;
block_id_type head_id;
struct signed_block_with_id {
signed_block_ptr ptr;
block_id_type id;
};
std::optional<signed_block_with_id> head;
virtual ~block_log_impl() = default;
@@ -482,16 +485,10 @@ namespace eosio { namespace chain {
virtual signed_block_ptr read_head() = 0;
void update_head(const signed_block_ptr& b, const std::optional<block_id_type>& id = {}) {
head = b;
if (id) {
head_id = *id;
} else {
if (head) {
head_id = b->calculate_id();
} else {
head_id = {};
}
}
if (b)
head = { b, id ? *id : b->calculate_id() };
else
head = {};
}
}; // block_log_impl
@@ -504,7 +501,7 @@ namespace eosio { namespace chain {
std::filesystem::remove(log_dir / "blocks.index");
}
uint32_t first_block_num() final { return head ? head->block_num() : first_block_number; }
uint32_t first_block_num() final { return head ? head->ptr->block_num() : first_block_number; }
void append(const signed_block_ptr& b, const block_id_type& id, const std::vector<char>& packed_block) final {
update_head(b, id);
}
@@ -591,7 +588,7 @@ namespace eosio { namespace chain {
}
uint64_t get_block_pos(uint32_t block_num) final {
if (!(head && block_num <= block_header::num_from_id(head_id) &&
if (!(head && block_num <= block_header::num_from_id(head->id) &&
block_num >= working_block_file_first_block_num()))
return block_log::npos;
index_file.seek(sizeof(uint64_t) * (block_num - index_first_block_num()));
@@ -707,7 +704,7 @@ namespace eosio { namespace chain {
uint32_t num_blocks;
this->block_file.seek_end(-sizeof(uint32_t));
fc::raw::unpack(this->block_file, num_blocks);
return this->head->block_num() - num_blocks + 1;
return this->head->ptr->block_num() - num_blocks + 1;
}
void reset(uint32_t first_bnum, std::variant<genesis_state, chain_id_type>&& chain_context, uint32_t version) {
@@ -740,7 +737,6 @@ namespace eosio { namespace chain {
this->reset(first_block_num, chain_id, block_log::max_supported_version);
this->head.reset();
head_id = {};
}
void flush() final {
@@ -804,7 +800,7 @@ namespace eosio { namespace chain {
size_t copy_from_pos = get_block_pos(first_block_num);
block_file.seek_end(-sizeof(uint32_t));
size_t copy_sz = block_file.tellp() - copy_from_pos;
const uint32_t num_blocks_in_log = chain::block_header::num_from_id(head_id) - first_block_num + 1;
const uint32_t num_blocks_in_log = chain::block_header::num_from_id(head->id) - first_block_num + 1;
const size_t offset_bytes = copy_from_pos - copy_to_pos;
const size_t offset_blocks = first_block_num - index_first_block_num;
@@ -992,7 +988,7 @@ namespace eosio { namespace chain {
block_file.close();
index_file.close();
catalog.add(preamble.first_block_num, this->head->block_num(), block_file.get_file_path().parent_path(),
catalog.add(preamble.first_block_num, this->head->ptr->block_num(), block_file.get_file_path().parent_path(),
"blocks");
using std::swap;
@@ -1007,7 +1003,7 @@ namespace eosio { namespace chain {
preamble.ver = block_log::max_supported_version;
preamble.chain_context = preamble.chain_id();
preamble.first_block_num = this->head->block_num() + 1;
preamble.first_block_num = this->head->ptr->block_num() + 1;
preamble.write_to(block_file);
}
@@ -1018,7 +1014,7 @@ namespace eosio { namespace chain {
}
void post_append(uint64_t pos) final {
if (head->block_num() % stride == 0) {
if (head->ptr->block_num() % stride == 0) {
split_log();
}
}
@@ -1121,7 +1117,7 @@ namespace eosio { namespace chain {
if ((pos & prune_config.prune_threshold) != (end & prune_config.prune_threshold))
num_blocks_in_log = prune(fc::log_level::debug);
else
num_blocks_in_log = chain::block_header::num_from_id(head_id) - first_block_number + 1;
num_blocks_in_log = chain::block_header::num_from_id(head->id) - first_block_number + 1;
fc::raw::pack(block_file, num_blocks_in_log);
}
@@ -1142,7 +1138,7 @@ namespace eosio { namespace chain {
uint32_t prune(const fc::log_level& loglevel) {
if (!head)
return 0;
const uint32_t head_num = chain::block_header::num_from_id(head_id);
const uint32_t head_num = chain::block_header::num_from_id(head->id);
if (head_num - first_block_number < prune_config.prune_blocks)
return head_num - first_block_number + 1;
@@ -1252,12 +1248,12 @@ namespace eosio { namespace chain {
signed_block_ptr block_log::head() const {
std::lock_guard g(my->mtx);
return my->head;
return my->head ? my->head->ptr : signed_block_ptr{};
}
block_id_type block_log::head_id() const {
std::optional<block_id_type> block_log::head_id() const {
std::lock_guard g(my->mtx);
return my->head_id;
return my->head ? my->head->id : std::optional<block_id_type>{};
}
uint32_t block_log::first_block_num() const {
+27 -55
View File
@@ -26,6 +26,7 @@
#include <eosio/chain/thread_utils.hpp>
#include <eosio/chain/platform_timer.hpp>
#include <eosio/chain/deep_mind.hpp>
#include <eosio/chain/wasm_interface_collection.hpp>
#include <chainbase/chainbase.hpp>
#include <eosio/vm/allocator.hpp>
@@ -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<fc::microseconds> 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<std::thread::id, std::unique_ptr<wasm_interface>> 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<scope_name,action_name> 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<digest_type>& cur_features,
@@ -342,12 +340,7 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::crypto_primitives>();
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);
});
@@ -416,10 +409,10 @@ struct controller_impl {
void log_irreversible() {
EOS_ASSERT( fork_db.root(), fork_database_exception, "fork database not properly initialized" );
const block_id_type log_head_id = blog.head_id();
const bool valid_log_head = !log_head_id.empty();
const std::optional<block_id_type> log_head_id = blog.head_id();
const bool valid_log_head = !!log_head_id;
const auto lib_num = valid_log_head ? block_header::num_from_id(log_head_id) : (blog.first_block_num() - 1);
const auto lib_num = valid_log_head ? block_header::num_from_id(*log_head_id) : (blog.first_block_num() - 1);
auto root_id = fork_db.root()->id;
@@ -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<wasm_interface>( 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();
}
@@ -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:
@@ -67,7 +67,7 @@ namespace eosio { namespace chain {
signed_block_ptr read_head()const; //use blocklog
signed_block_ptr head()const;
block_id_type head_id()const;
std::optional<block_id_type> head_id()const;
uint32_t first_block_num() const;
@@ -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();
@@ -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
@@ -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;
};
} }
@@ -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<struct wasm_interface_impl> 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) )
@@ -0,0 +1,85 @@
#pragma once
#include <eosio/chain/wasm_interface.hpp>
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_interface>(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<std::thread::id, std::unique_ptr<wasm_interface>> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit
};
} // eosio::chain
@@ -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<webassembly::eos_vm_runtime::eos_vm_runtime<eosio::vm::interpreter>>();
@@ -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_tier> eosvmoc;
@@ -15,7 +15,6 @@
#include <thread>
#include <shared_mutex>
namespace std {
template<> struct hash<eosio::chain::eosvmoc::code_tuple> {
@@ -39,7 +38,7 @@ struct config;
class code_cache_base {
public:
code_cache_base(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db);
code_cache_base(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db);
~code_cache_base();
const int& fd() const { return _cache_fd; }
@@ -78,9 +77,20 @@ class code_cache_base {
local::datagram_protocol::socket _compile_monitor_write_socket{_ctx};
local::datagram_protocol::socket _compile_monitor_read_socket{_ctx};
//these are really only useful to the async code cache, but keep them here so
//free_code can be shared
std::unordered_set<code_tuple> _queued_compiles;
//these are really only useful to the async code cache, but keep them here so free_code can be shared
using queued_compilies_t = boost::multi_index_container<
code_tuple,
indexed_by<
sequenced<>,
hashed_unique<tag<by_hash>,
composite_key< code_tuple,
member<code_tuple, digest_type, &code_tuple::code_id>,
member<code_tuple, uint8_t, &code_tuple::vm_version>
>
>
>
>;
queued_compilies_t _queued_compiles;
std::unordered_map<code_tuple, bool> _outstanding_compiles_and_poison;
size_t _free_bytes_eviction_threshold;
@@ -95,13 +105,13 @@ class code_cache_base {
class code_cache_async : public code_cache_base {
public:
code_cache_async(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db);
code_cache_async(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db);
~code_cache_async();
//If code is in cache: returns pointer & bumps to front of MRU list
//If code is not in cache, and not blacklisted, and not currently compiling: return nullptr and kick off compile
//otherwise: return nullptr
const code_descriptor* const get_descriptor_for_code(const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure);
const code_descriptor* const get_descriptor_for_code(bool high_priority, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure);
private:
std::thread _monitor_reply_thread;
+8 -5
View File
@@ -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,11 +90,12 @@ 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 {
cd = my->eosvmoc->cc.get_descriptor_for_code(code_hash, vm_version, context.control.is_write_window(), failure);
const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name;
cd = my->eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure);
}
catch(...) {
//swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline
@@ -105,6 +106,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;
}
@@ -38,7 +38,7 @@ static constexpr size_t descriptor_ptr_from_file_start = header_offset + offseto
static_assert(sizeof(code_cache_header) <= header_size, "code_cache_header too big");
code_cache_async::code_cache_async(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) :
code_cache_async::code_cache_async(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) :
code_cache_base(data_dir, eosvmoc_config, db),
_result_queue(eosvmoc_config.threads * 2),
_threads(eosvmoc_config.threads)
@@ -106,7 +106,7 @@ std::tuple<size_t, size_t> code_cache_async::consume_compile_thread_queue() {
}
const code_descriptor* const code_cache_async::get_descriptor_for_code(const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) {
const code_descriptor* const code_cache_async::get_descriptor_for_code(bool high_priority, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) {
//if there are any outstanding compiles, process the result queue now
//When app is in write window, all tasks are running sequentially and read-only threads
//are not running. Safe to update cache entries.
@@ -156,13 +156,16 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const dig
it->second = false;
return nullptr;
}
if(_queued_compiles.find(ct) != _queued_compiles.end()) {
if(auto it = _queued_compiles.get<by_hash>().find(boost::make_tuple(std::ref(code_id), vm_version)); it != _queued_compiles.get<by_hash>().end()) {
failure = get_cd_failure::temporary; // Compile might not be done yet
return nullptr;
}
if(_outstanding_compiles_and_poison.size() >= _threads) {
_queued_compiles.emplace(ct);
if (high_priority)
_queued_compiles.push_front(ct);
else
_queued_compiles.push_back(ct);
failure = get_cd_failure::temporary; // Compile might not be done yet
return nullptr;
}
@@ -221,7 +224,7 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const
return &*_cache_index.push_front(std::move(std::get<code_descriptor>(result.result))).first;
}
code_cache_base::code_cache_base(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) :
code_cache_base::code_cache_base(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) :
_db(db),
_cache_file_path(data_dir/"code_cache.bin")
{
@@ -377,7 +380,8 @@ void code_cache_base::free_code(const digest_type& code_id, const uint8_t& vm_ve
}
//if it's in the queued list, erase it
_queued_compiles.erase({code_id, vm_version});
if(auto i = _queued_compiles.get<by_hash>().find(boost::make_tuple(std::ref(code_id), vm_version)); i != _queued_compiles.get<by_hash>().end())
_queued_compiles.get<by_hash>().erase(i);
//however, if it's currently being compiled there is no way to cancel the compile,
//so instead set a poison boolean that indicates not to insert the code in to the cache
+168 -183
View File
@@ -32,94 +32,53 @@ inline std::pair<std::string, std::string> split_host_port(std::string_view endp
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
template <typename Protocol>
struct listener_base;
template <>
struct listener_base<boost::asio::ip::tcp> {
listener_base(const std::string&) {}
};
template <>
struct listener_base<boost::asio::local::stream_protocol> {
std::filesystem::path path_;
listener_base(const std::string& local_address) : path_(std::filesystem::absolute(local_address)) {}
~listener_base() {
std::error_code ec;
std::filesystem::remove(path_, ec);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// fc::listener is template class to simplify the code for accepting new socket connections.
/// @brief fc::listener is template class to simplify the code for accepting new socket connections.
/// It can be used for both tcp or Unix socket connection.
///
/// Example Usage:
/// \code{.cpp}
///
/// class shared_state_type;
///
/// template <typename Protocol>
/// struct example_session : std::enable_shared_from_this<example_session<Protocol>> {
/// using socket_type = Protocol::socket;
/// socket_type&& socket_;
/// shared_state_type& shared_state_;
/// example_session(socket_type&& socket, shared_state_type& shared_state)
/// : socket_(std::move(socket)), shared_state_(shared_state_) {}
///
/// // ...
/// void start();
/// };
///
/// template <typename Protocol>
/// struct example_listener : fc::listener<example_listener<Protocol>, Protocol>{
/// static constexpr uint32_t accept_timeout_ms = 200;
/// shared_state_type& shared_state_;
///
/// example_listener(boost::asio::io_context& executor,
/// logger& logger,
/// const std::string& local_address,
/// const typename Protocol::endpoint& endpoint,
/// shared_state_type& shared_state)
/// : fc::listener<example_listener<Protocol>, Protocol>
/// (executor, logger, boost::posix_time::milliseconds(accept_timeout_ms), local_address, endpoint)
/// , shared_state_(shared_state) {}
///
/// std::string extra_listening_log_info() {
/// return shared_state_.info_to_be_printed_after_address_is_resolved_and_listening;
/// }
///
/// void create_session(Protocol::socket&& sock) {
/// auto session = std::make_shared<example_session>(std::move(sock), shared_state_);
/// session->start();
/// }
/// };
///
/// int main() {
/// boost::asio::io_context ioc;
/// fc::logger logger = fc::logger::get(DEFAULT_LOGGER);
/// shared_state_type shared_state{...};
///
/// // usage for accepting tcp connection
/// // notice that it only throws std::system_error, not fc::exception
/// example_listener<boost::asio::ip::tcp>::create(executor, logger, "localhost:8080", std::ref(shared_state));
///
/// // usage for accepting unix socket connection
/// example_listener<boost::asio::local::stream_protocol>::create(executor, logger, "tmp.sock",
/// std::ref(shared_state));
///
/// ioc.run();
/// return 0;
/// }
/// \endcode
/// @note Users should use fc::create_listener() instead, this class is the implementation
/// detail for fc::create_listener().
///
/////////////////////////////////////////////////////////////////////////////////////////////
template <typename T, typename Protocol>
struct listener : std::enable_shared_from_this<T> {
using endpoint_type = typename Protocol::endpoint;
template <typename Protocol, typename CreateSession>
struct listener : listener_base<Protocol>, std::enable_shared_from_this<listener<Protocol, CreateSession>> {
private:
typename Protocol::acceptor acceptor_;
boost::asio::deadline_timer accept_error_timer_;
boost::posix_time::time_duration accept_timeout_;
logger& logger_;
std::string local_address_;
std::string extra_listening_log_info_;
CreateSession create_session_;
public:
using endpoint_type = typename Protocol::endpoint;
listener(boost::asio::io_context& executor, logger& logger, boost::posix_time::time_duration accept_timeout,
const std::string& local_address, const endpoint_type& endpoint)
: acceptor_(executor, endpoint), accept_error_timer_(executor), accept_timeout_(accept_timeout), logger_(logger),
local_address_(std::is_same_v<Protocol, boost::asio::ip::tcp>
? local_address
: std::filesystem::absolute(local_address).string()) {}
const std::string& local_address, const endpoint_type& endpoint,
const std::string& extra_listening_log_info, const CreateSession& create_session)
: listener_base<Protocol>(local_address), acceptor_(executor, endpoint), accept_error_timer_(executor),
accept_timeout_(accept_timeout), logger_(logger), extra_listening_log_info_(extra_listening_log_info),
create_session_(create_session) {}
~listener() {
if constexpr (std::is_same_v<Protocol, boost::asio::local::stream_protocol>) {
std::filesystem::remove(local_address_);
}
}
const auto& acceptor() const { return acceptor_; }
void do_accept() {
acceptor_.async_accept([self = this->shared_from_this()](boost::system::error_code ec, auto&& peer_socket) {
@@ -130,7 +89,7 @@ struct listener : std::enable_shared_from_this<T> {
template <typename Socket>
void on_accept(boost::system::error_code ec, Socket&& socket) {
if (!ec) {
static_cast<T*>(this)->create_session(std::forward<Socket>(socket));
create_session_(std::forward<Socket>(socket));
do_accept();
} else if (ec == boost::system::errc::too_many_files_open) {
// retry accept() after timeout to avoid cpu loop on accept
@@ -161,8 +120,6 @@ struct listener : std::enable_shared_from_this<T> {
}
}
const char* extra_listening_log_info() { return ""; }
void log_listening(const endpoint_type& endpoint, const std::string& local_address) {
std::string info;
if constexpr (std::is_same_v<Protocol, boost::asio::ip::tcp>) {
@@ -170,111 +127,139 @@ struct listener : std::enable_shared_from_this<T> {
} else {
info = "Unix socket " + local_address;
}
info += static_cast<T*>(this)->extra_listening_log_info();
info += extra_listening_log_info_;
fc_ilog(logger_, "start listening on ${info}", ("info", info));
}
/// @brief Create listeners to listen on endpoints resolved from address
/// @param ...args The arguments to forward to the listener constructor so that they can be accessed
/// from create_session() to construct the customized session objects.
/// @throws std::system_error
template <typename... Args>
static void create(boost::asio::io_context& executor, logger& logger, const std::string& address, Args&&... args) {
using tcp = boost::asio::ip::tcp;
if constexpr (std::is_same_v<Protocol, tcp>) {
auto [host, port] = split_host_port(address);
if (port.empty()) {
fc_elog(logger, "port is not specified for address ${addr}", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::bad_address));
}
boost::system::error_code ec;
tcp::resolver resolver(executor);
auto endpoints = resolver.resolve(host, port, tcp::resolver::passive, ec);
if (ec) {
fc_elog(logger, "failed to resolve address: ${msg}", ("msg", ec.message()));
throw std::system_error(ec);
}
int listened = 0;
std::optional<tcp::endpoint> unspecified_ipv4_addr;
bool has_unspecified_ipv6_only = false;
auto create_server = [&](const auto& endpoint) {
const auto& ip_addr = endpoint.address();
try {
auto server = std::make_shared<T>(executor, logger, address, endpoint, std::forward<Args&&>(args)...);
server->log_listening(endpoint, address);
server->do_accept();
++listened;
has_unspecified_ipv6_only = ip_addr.is_unspecified() && ip_addr.is_v6();
if (has_unspecified_ipv6_only) {
boost::asio::ip::v6_only option;
server->acceptor_.get_option(option);
has_unspecified_ipv6_only &= option.value();
}
} catch (boost::system::system_error& ex) {
fc_wlog(logger, "unable to listen on ${ip_addr}:${port} resolved from ${address}: ${msg}",
("ip_addr", ip_addr.to_string())("port", endpoint.port())("address", address)("msg", ex.what()));
}
};
for (const auto& ep : endpoints) {
const auto& endpoint = ep.endpoint();
const auto& ip_addr = endpoint.address();
if (ip_addr.is_unspecified() && ip_addr.is_v4() && endpoints.size() > 1) {
// it is an error to bind a socket to the same port for both ipv6 and ipv4 INADDR_ANY address when
// the system has ipv4-mapped ipv6 enabled by default, we just skip the ipv4 for now.
unspecified_ipv4_addr = endpoint;
continue;
}
create_server(endpoint);
}
if (unspecified_ipv4_addr.has_value() && has_unspecified_ipv6_only) {
create_server(*unspecified_ipv4_addr);
}
if (listened == 0) {
fc_elog(logger, "none of the addresses resolved from ${addr} can be listened to", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::bad_address));
}
} else {
using stream_protocol = boost::asio::local::stream_protocol;
static_assert(std::is_same_v<Protocol, stream_protocol>);
namespace fs = std::filesystem;
auto cwd = fs::current_path();
fs::path sock_path = address;
fs::create_directories(sock_path.parent_path());
// The maximum length of the socket path is defined by sockaddr_un::sun_path. On Linux,
// according to unix(7), it is 108 bytes. On FreeBSD, according to unix(4), it is 104 bytes.
// Therefore, we create the unix socket with the relative path to its parent path to avoid the
// problem.
fs::current_path(sock_path.parent_path());
auto restore = fc::make_scoped_exit([cwd] { fs::current_path(cwd); });
endpoint_type endpoint{ sock_path.filename().string() };
boost::system::error_code ec;
stream_protocol::socket test_socket(executor);
test_socket.connect(endpoint, ec);
// looks like a service is already running on that socket, don't touch it... fail out
if (ec == boost::system::errc::success) {
fc_elog(logger, "The unix socket path ${addr} is already in use", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::address_in_use));
}
// socket exists but no one home, go ahead and remove it and continue on
else if (ec == boost::system::errc::connection_refused)
fs::remove(sock_path);
auto server = std::make_shared<T>(executor, logger, address, endpoint, std::forward<Args&&>(args)...);
server->log_listening(endpoint, address);
server->do_accept();
}
}
};
/// @brief create a stream-oriented socket listener which listens on the specified \c address and calls \c
/// create_session whenever a socket is accepted.
///
/// @details
/// This function is used for listening on TCP or Unix socket address and creating corresponding session when the
/// socket is accepted.
///
/// For TCP socket, the address format can be <hostname>:port or <ipaddress>:port where the `:port` part is mandatory.
/// If only the port is specified, all network interfaces are listened. The function can listen on multiple IP addresses
/// if the specified hostname is resolved to multiple IP addresses; in other words, it can create more than one
/// fc::listener objects. If port is not specified or none of the resolved address can be listened, an std::system_error
/// with std::errc::bad_address error code will be thrown.
///
/// For Unix socket, this function will temporary change current working directory to the parent of the specified \c
/// address (i.e. socket file path), listen on the filename component of the path, and then restore the working
/// directory before return. This is the workaround for the socket file paths limitation which is around 100 characters.
///
/// The lifetime of the created listener objects is controlled by \c executor, the created objects will be destroyed
/// when \c executor.stop() is called.
///
/// @note
/// This function is not thread safe for Unix socket because it will temporarily change working directory without any
/// lock. Any code which depends the current working directory (such as opening files with relative paths) in other
/// threads should be protected.
///
/// @tparam Protocol either \c boost::asio::ip::tcp or \c boost::asio::local::stream_protocol
/// @throws std::system_error or boost::system::system_error
template <typename Protocol, typename CreateSession>
void create_listener(boost::asio::io_context& executor, logger& logger, boost::posix_time::time_duration accept_timeout,
const std::string& address, const std::string& extra_listening_log_info,
const CreateSession& create_session) {
using tcp = boost::asio::ip::tcp;
if constexpr (std::is_same_v<Protocol, tcp>) {
auto [host, port] = split_host_port(address);
if (port.empty()) {
fc_elog(logger, "port is not specified for address ${addr}", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::bad_address));
}
boost::system::error_code ec;
tcp::resolver resolver(executor);
auto endpoints = resolver.resolve(host, port, tcp::resolver::passive, ec);
if (ec) {
fc_elog(logger, "failed to resolve address: ${msg}", ("msg", ec.message()));
throw std::system_error(ec);
}
int listened = 0;
std::optional<tcp::endpoint> unspecified_ipv4_addr;
bool has_unspecified_ipv6_only = false;
auto create_listener = [&](const auto& endpoint) {
const auto& ip_addr = endpoint.address();
try {
auto listener = std::make_shared<fc::listener<Protocol, CreateSession>>(
executor, logger, accept_timeout, address, endpoint, extra_listening_log_info, create_session);
listener->log_listening(endpoint, address);
listener->do_accept();
++listened;
has_unspecified_ipv6_only = ip_addr.is_unspecified() && ip_addr.is_v6();
if (has_unspecified_ipv6_only) {
boost::asio::ip::v6_only option;
listener->acceptor().get_option(option);
has_unspecified_ipv6_only &= option.value();
}
} catch (boost::system::system_error& ex) {
fc_wlog(logger, "unable to listen on ${ip_addr}:${port} resolved from ${address}: ${msg}",
("ip_addr", ip_addr.to_string())("port", endpoint.port())("address", address)("msg", ex.what()));
}
};
for (const auto& ep : endpoints) {
const auto& endpoint = ep.endpoint();
const auto& ip_addr = endpoint.address();
if (ip_addr.is_unspecified() && ip_addr.is_v4() && endpoints.size() > 1) {
// it is an error to bind a socket to the same port for both ipv6 and ipv4 INADDR_ANY address when
// the system has ipv4-mapped ipv6 enabled by default, we just skip the ipv4 for now.
unspecified_ipv4_addr = endpoint;
continue;
}
create_listener(endpoint);
}
if (unspecified_ipv4_addr.has_value() && has_unspecified_ipv6_only) {
create_listener(*unspecified_ipv4_addr);
}
if (listened == 0) {
fc_elog(logger, "none of the addresses resolved from ${addr} can be listened to", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::bad_address));
}
} else {
using stream_protocol = boost::asio::local::stream_protocol;
static_assert(std::is_same_v<Protocol, stream_protocol>);
namespace fs = std::filesystem;
auto cwd = fs::current_path();
fs::path sock_path = address;
fs::create_directories(sock_path.parent_path());
// The maximum length of the socket path is defined by sockaddr_un::sun_path. On Linux,
// according to unix(7), it is 108 bytes. On FreeBSD, according to unix(4), it is 104 bytes.
// Therefore, we create the unix socket with the relative path to its parent path to avoid the
// problem.
fs::current_path(sock_path.parent_path());
auto restore = fc::make_scoped_exit([cwd] { fs::current_path(cwd); });
stream_protocol::endpoint endpoint{ sock_path.filename().string() };
boost::system::error_code ec;
stream_protocol::socket test_socket(executor);
test_socket.connect(endpoint, ec);
// looks like a service is already running on that socket, don't touch it... fail out
if (ec == boost::system::errc::success) {
fc_elog(logger, "The unix socket path ${addr} is already in use", ("addr", address));
throw std::system_error(std::make_error_code(std::errc::address_in_use));
}
else if (ec == boost::system::errc::connection_refused) {
// socket exists but no one home, go ahead and remove it and continue on
fs::remove(sock_path);
}
auto listener = std::make_shared<fc::listener<stream_protocol, CreateSession>>(
executor, logger, accept_timeout, address, endpoint, extra_listening_log_info, create_session);
listener->log_listening(endpoint, address);
listener->do_accept();
}
}
} // namespace fc
@@ -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
@@ -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;
+38 -6
View File
@@ -114,6 +114,32 @@ void validate(boost::any& v,
}
}
void validate(boost::any& v,
const std::vector<std::string>& 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<eosio::chain::validation_mode>();
app().register_config_type<chainbase::pinnable_mapped_file::map_mode>();
app().register_config_type<eosio::chain::wasm_interface::vm_type>();
app().register_config_type<eosio::chain::wasm_interface::vm_oc_enable>();
}
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,16 +361,22 @@ 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<chain::wasm_interface::vm_oc_enable>()->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<bool>()->default_value(false), "enable queries to find accounts by various metadata.")
("max-nonprivileged-inline-action-size", bpo::value<uint32_t>()->default_value(config::default_max_nonprivileged_inline_action_size), "maximum allowed size (in bytes) of an inline action for a nonprivileged account")
("transaction-retry-max-storage-size-gb", bpo::value<uint64_t>(),
"Maximum size (in GiB) allowed to be allocated for the Transaction Retry feature. Setting above 0 enables this feature.")
("transaction-retry-interval-sec", bpo::value<uint32_t>()->default_value(20),
"How often, in seconds, to resend an incoming transaction to network if not seen in a block.")
"How often, in seconds, to resend an incoming transaction to network if not seen in a block.\n"
"Needs to be at least twice as large as p2p-dedup-cache-expire-time-sec.")
("transaction-retry-max-expiration-sec", bpo::value<uint32_t>()->default_value(120),
"Maximum allowed transaction expiration for retry transactions, will retry transactions up to this value.")
"Maximum allowed transaction expiration for retry transactions, will retry transactions up to this value.\n"
"Should be larger than transaction-retry-interval-sec.")
("transaction-finality-status-max-storage-size-gb", bpo::value<uint64_t>(),
"Maximum size (in GiB) allowed to be allocated for the Transaction Finality Status feature. Setting above 0 enables this feature.")
("transaction-finality-status-success-duration-sec", bpo::value<uint64_t>()->default_value(config::default_max_transaction_finality_status_success_duration_sec),
@@ -907,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<uint64_t>() * 1024u * 1024u;
if( options.count("eos-vm-oc-compile-threads") )
chain_config->eosvmoc_config.threads = options.at("eos-vm-oc-compile-threads").as<uint64_t>();
if( options["eos-vm-oc-enable"].as<bool>() )
chain_config->eosvmoc_tierup = true;
chain_config->eosvmoc_tierup = options["eos-vm-oc-enable"].as<chain::wasm_interface::vm_oc_enable>();
#endif
account_queries_enabled = options.at("enable-account-queries").as<bool>();
+1 -1
View File
@@ -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})
@@ -5,8 +5,8 @@
#include <stdint.h>
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 = {
+25 -32
View File
@@ -111,34 +111,6 @@ namespace eosio {
return result;
}
template <typename Protocol>
struct beast_http_listener
: fc::listener<beast_http_listener<Protocol>, Protocol> {
using socket_type = typename Protocol::socket;
static constexpr uint32_t accept_timeout_ms = 500;
http_plugin_state& state_;
api_category_set categories_ = {};
beast_http_listener(boost::asio::io_context& executor, fc::logger& logger, const std::string& local_address,
const typename Protocol::endpoint& endpoint, http_plugin_state& plugin_state,
api_category_set categories)
: fc::listener<beast_http_listener<Protocol>, Protocol>(
executor, logger, boost::posix_time::milliseconds(accept_timeout_ms), local_address, endpoint),
state_(plugin_state), categories_(categories) {}
std::string extra_listening_log_info() { return " for API categories: " + category_names(categories_); }
void create_session(socket_type&& socket) {
boost::system::error_code re_ec;
auto re = socket.remote_endpoint(re_ec);
std::string remote_endpoint = re_ec ? "unknown" : boost::lexical_cast<std::string>(re);
std::make_shared<beast_http_session<socket_type>>(std::move(socket), this->state_, std::move(remote_endpoint),
categories_, this->local_address_)
->run_session();
}
};
class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
public:
http_plugin_impl() = default;
@@ -241,6 +213,29 @@ namespace eosio {
});
}
template <typename Protocol>
void create_listener(const std::string& address, api_category_set categories) {
const boost::posix_time::milliseconds accept_timeout(500);
auto extra_listening_log_info = " for API categories: " + category_names(categories);
using socket_type = typename Protocol::socket;
auto create_session = [this, categories, address](socket_type&& socket) {
std::string remote_endpoint;
if constexpr (std::is_same_v<socket_type, tcp>) {
boost::system::error_code re_ec;
auto re = socket.remote_endpoint(re_ec);
remote_endpoint = re_ec ? "unknown" : fc::to_string(re);
} else {
remote_endpoint = address;
}
std::make_shared<beast_http_session<socket_type>>(
std::move(socket), plugin_state, std::move(remote_endpoint), categories, address)
->run_session();
};
fc::create_listener<Protocol>(plugin_state.thread_pool.get_executor(), logger(), accept_timeout, address,
extra_listening_log_info, create_session);
}
void create_beast_server(const std::string& address, api_category_set categories) {
try {
if (is_unix_socket_address(address)) {
@@ -248,11 +243,9 @@ namespace eosio {
fs::path sock_path = address;
if (sock_path.is_relative())
sock_path = fs::weakly_canonical(app().data_dir() / sock_path);
beast_http_listener<boost::asio::local::stream_protocol>::create(plugin_state.thread_pool.get_executor(), logger(),
sock_path.string(), std::ref(plugin_state), categories);
create_listener<boost::asio::local::stream_protocol>(sock_path.string(), categories);
} else {
beast_http_listener<tcp>::create(plugin_state.thread_pool.get_executor(), logger(), address,
std::ref(plugin_state), categories);
create_listener<tcp>(address, categories);
}
} catch (const fc::exception& e) {
fc_elog(logger(), "http service failed to start for ${addr}: ${e}",
+1 -1
View File
@@ -354,7 +354,7 @@ class app_log {
int fork_app_and_redirect_stderr(const char* redirect_filename, std::initializer_list<const char*> args) {
int pid = fork();
if (pid == 0) {
freopen(redirect_filename, "w", stderr);
(void) freopen(redirect_filename, "w", stderr);
bool ret = 0;
try {
appbase::scoped_app app;
+55 -60
View File
@@ -517,6 +517,8 @@ namespace eosio {
void plugin_shutdown();
bool in_sync() const;
fc::logger& get_logger() { return logger; }
void create_session(tcp::socket&& socket);
};
// peer_[x]log must be called from thread in connection strand
@@ -2647,68 +2649,54 @@ namespace eosio {
} ) );
}
struct p2p_listener : public fc::listener<p2p_listener, tcp> {
static constexpr uint32_t accept_timeout_ms = 100;
eosio::net_plugin_impl* state_;
p2p_listener(boost::asio::io_context& executor, fc::logger& logger, const std::string& local_address,
const tcp::endpoint& endpoint, eosio::net_plugin_impl* impl)
: fc::listener<p2p_listener, tcp>(executor, logger, boost::posix_time::milliseconds(accept_timeout_ms),
local_address, endpoint),
state_(impl) {}
std::string extra_listening_log_info() {
return ", max clients is " + std::to_string(state_->connections.get_max_client_count());
}
void create_session(tcp::socket&& socket) {
uint32_t visitors = 0;
uint32_t from_addr = 0;
boost::system::error_code rec;
const auto& paddr_add = socket.remote_endpoint(rec).address();
string paddr_str;
if (rec) {
fc_elog(logger, "Error getting remote endpoint: ${m}", ("m", rec.message()));
} else {
paddr_str = paddr_add.to_string();
state_->connections.for_each_connection([&visitors, &from_addr, &paddr_str](auto& conn) {
if (conn->socket_is_open()) {
if (conn->peer_address().empty()) {
++visitors;
fc::lock_guard g_conn(conn->conn_mtx);
if (paddr_str == conn->remote_endpoint_ip) {
++from_addr;
}
void net_plugin_impl::create_session(tcp::socket&& socket) {
uint32_t visitors = 0;
uint32_t from_addr = 0;
boost::system::error_code rec;
const auto& paddr_add = socket.remote_endpoint(rec).address();
string paddr_str;
if (rec) {
fc_elog(logger, "Error getting remote endpoint: ${m}", ("m", rec.message()));
} else {
paddr_str = paddr_add.to_string();
connections.for_each_connection([&visitors, &from_addr, &paddr_str](auto& conn) {
if (conn->socket_is_open()) {
if (conn->peer_address().empty()) {
++visitors;
fc::lock_guard g_conn(conn->conn_mtx);
if (paddr_str == conn->remote_endpoint_ip) {
++from_addr;
}
}
}
});
if (from_addr < max_nodes_per_host &&
(auto_bp_peering_enabled() || connections.get_max_client_count() == 0 ||
visitors < connections.get_max_client_count())) {
fc_ilog(logger, "Accepted new connection: " + paddr_str);
connection_ptr new_connection = std::make_shared<connection>(std::move(socket));
new_connection->strand.post([new_connection, this]() {
if (new_connection->start_session()) {
connections.add(new_connection);
}
});
if (from_addr < state_->max_nodes_per_host &&
(state_->auto_bp_peering_enabled() || state_->connections.get_max_client_count() == 0 ||
visitors < state_->connections.get_max_client_count())) {
fc_ilog(logger, "Accepted new connection: " + paddr_str);
connection_ptr new_connection = std::make_shared<connection>(std::move(socket));
new_connection->strand.post([new_connection, state = state_]() {
if (new_connection->start_session()) {
state->connections.add(new_connection);
}
});
} else {
if (from_addr >= max_nodes_per_host) {
fc_dlog(logger, "Number of connections (${n}) from ${ra} exceeds limit ${l}",
("n", from_addr + 1)("ra", paddr_str)("l", max_nodes_per_host));
} else {
if (from_addr >= state_->max_nodes_per_host) {
fc_dlog(logger, "Number of connections (${n}) from ${ra} exceeds limit ${l}",
("n", from_addr + 1)("ra", paddr_str)("l", state_->max_nodes_per_host));
} else {
fc_dlog(logger, "max_client_count ${m} exceeded", ("m", state_->connections.get_max_client_count()));
}
// new_connection never added to connections and start_session not called, lifetime will end
boost::system::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec);
socket.close(ec);
fc_dlog(logger, "max_client_count ${m} exceeded", ("m", connections.get_max_client_count()));
}
// new_connection never added to connections and start_session not called, lifetime will end
boost::system::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec);
socket.close(ec);
}
}
};
}
// only called from strand thread
void connection::start_read_message() {
@@ -3105,8 +3093,8 @@ namespace eosio {
my_impl->mark_bp_connection(this);
if (my_impl->exceeding_connection_limit(this)) {
// When auto bp peering is enabled, the p2p_listener check doesn't have enough information to determine
// if a client is a BP peer. In p2p_listener, it only has the peer address which a node is connecting
// When auto bp peering is enabled, create_session() check doesn't have enough information to determine
// if a client is a BP peer. In create_session(), it only has the peer address which a node is connecting
// from, but it would be different from the address it is listening. The only way to make sure is when the
// first handshake message is received with the p2p_address information in the message. Thus the connection
// limit checking has to be here when auto bp peering is enabled.
@@ -3371,14 +3359,14 @@ namespace eosio {
}
switch (msg.known_trx.mode) {
case none:
break;
case last_irr_catch_up:
case catch_up : {
case last_irr_catch_up: {
fc::unique_lock g_conn( conn_mtx );
last_handshake_recv.head_num = msg.known_blocks.pending;
last_handshake_recv.head_num = std::max(msg.known_blocks.pending, last_handshake_recv.head_num);
g_conn.unlock();
break;
}
case catch_up:
break;
case normal: {
my_impl->dispatcher->recv_notice( shared_from_this(), msg, false );
}
@@ -4099,7 +4087,14 @@ namespace eosio {
app().executor().post(priority::highest, [my=shared_from_this(), address = std::move(listen_address)](){
if (address.size()) {
try {
p2p_listener::create(my->thread_pool.get_executor(), logger, address, my.get());
const boost::posix_time::milliseconds accept_timeout(100);
std::string extra_listening_log_info =
", max clients is " + std::to_string(my->connections.get_max_client_count());
fc::create_listener<tcp>(
my->thread_pool.get_executor(), logger, accept_timeout, address, extra_listening_log_info,
[my = my](tcp::socket&& socket) { my->create_session(std::move(socket)); });
} catch (const std::exception& e) {
fc_elog( logger, "net_plugin::plugin_startup failed to listen on ${addr}, ${what}",
("addr", address)("what", e.what()) );
+3 -1
View File
@@ -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<std::string> key_spec_pairs = options["signature-provider"].as<std::vector<std::string>>();
+1 -1
View File
@@ -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})
@@ -52,8 +52,8 @@ BOOST_AUTO_TEST_SUITE(read_only_trxs)
enum class app_init_status { failed, succeeded };
void test_configs_common(std::vector<const char*>& 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<const char*>& 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<const char*> 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<const char*> 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",
@@ -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();
{
@@ -142,7 +142,34 @@ public:
return head_timestamp;
}
void listen();
template <typename Protocol>
void create_listener(const std::string& address) {
const boost::posix_time::milliseconds accept_timeout(200);
using socket_type = typename Protocol::socket;
fc::create_listener<Protocol>(
thread_pool.get_executor(), _log, accept_timeout, address, "", [this](socket_type&& socket) {
// Create a session object and run it
catch_and_log([&, this] {
auto s = std::make_shared<session<state_history_plugin_impl, socket_type>>(*this, std::move(socket),
session_mgr);
session_mgr.insert(s);
s->start();
});
});
}
void listen(){
try {
if (!endpoint_address.empty()) {
create_listener<boost::asio::ip::tcp>(endpoint_address);
}
if (!unix_path.empty()) {
create_listener<boost::asio::local::stream_protocol>(unix_path);
}
} catch (std::exception&) {
FC_THROW_EXCEPTION(plugin_exception, "unable to open listen socket");
}
}
// called from main thread
void on_applied_transaction(const transaction_trace_ptr& p, const packed_transaction_ptr& t) {
@@ -231,44 +258,6 @@ public:
}; // state_history_plugin_impl
template <typename Protocol>
struct ship_listener : fc::listener<ship_listener<Protocol>, Protocol> {
using socket_type = typename Protocol::socket;
static constexpr uint32_t accept_timeout_ms = 200;
state_history_plugin_impl& state_;
ship_listener(boost::asio::io_context& executor, logger& logger, const std::string& local_address,
const typename Protocol::endpoint& endpoint, state_history_plugin_impl& state)
: fc::listener<ship_listener<Protocol>, Protocol>(
executor, logger, boost::posix_time::milliseconds(accept_timeout_ms), local_address, endpoint)
, state_(state) {}
void create_session(socket_type&& socket) {
// Create a session object and run it
catch_and_log([&] {
auto s = std::make_shared<session<state_history_plugin_impl, socket_type>>(
state_, std::move(socket), state_.get_session_manager());
state_.get_session_manager().insert(s);
s->start();
});
}
};
void state_history_plugin_impl::listen() {
try {
if (!endpoint_address.empty()) {
ship_listener<boost::asio::ip::tcp>::create(thread_pool.get_executor(), _log, endpoint_address, *this);
}
if (!unix_path.empty()) {
ship_listener<boost::asio::local::stream_protocol>::create(thread_pool.get_executor(), _log, unix_path, *this);
}
} catch (std::exception&) {
FC_THROW_EXCEPTION(plugin_exception, "unable to open listen socket");
}
}
state_history_plugin::state_history_plugin()
: my(std::make_shared<state_history_plugin_impl>()) {}
@@ -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)
@@ -0,0 +1,2 @@
#define BOOST_TEST_MODULE state_history_plugin
#include <boost/test/included/unit_test.hpp>
@@ -1,5 +1,3 @@
#define BOOST_TEST_MODULE example
#include <boost/test/unit_test.hpp>
#include <algorithm>
@@ -145,23 +143,6 @@ struct mock_state_history_plugin {
using session_type = eosio::session<mock_state_history_plugin, tcp::socket>;
struct listener : fc::listener<listener, tcp> {
mock_state_history_plugin& server_;
listener(boost::asio::io_context& executor, fc::logger& logger, const std::string& local_address,
tcp::endpoint& endpoint, mock_state_history_plugin& server)
: fc::listener<listener, tcp>(executor, logger, boost::posix_time::milliseconds(100), local_address, endpoint)
, server_(server) {
endpoint = acceptor_.local_endpoint();
}
void create_session(tcp::socket&& peer_socket) {
auto s = std::make_shared<session_type>(server_, std::move(peer_socket), server_.session_mgr);
s->start();
server_.add_session(s);
}
};
struct test_server : mock_state_history_plugin {
std::vector<std::thread> threads;
tcp::endpoint local_address{net::ip::make_address("127.0.0.1"), 0};
@@ -174,8 +155,17 @@ struct test_server : mock_state_history_plugin {
threads.emplace_back([this]{ main_ioc.run(); });
threads.emplace_back([this]{ ship_ioc.run(); });
auto create_session = [this](tcp::socket&& peer_socket) {
auto s = std::make_shared<session_type>(*this, std::move(peer_socket), session_mgr);
s->start();
add_session(s);
};
// Create and launch a listening port
std::make_shared<listener>(ship_ioc, logger, "", local_address, *this)->do_accept();
auto server = std::make_shared<fc::listener<tcp, decltype(create_session)>>(
ship_ioc, logger, boost::posix_time::milliseconds(100), "", local_address, "", create_session);
server->do_accept();
local_address = server->acceptor().local_endpoint();
}
~test_server() {
+1
View File
@@ -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 {
+4 -2
View File
@@ -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})
+10
View File
@@ -537,6 +537,16 @@ class Node(Transactions):
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)
+2 -1
View File
@@ -67,7 +67,8 @@ struct block_log_fixture {
void check_range_present(uint32_t first, uint32_t last) {
BOOST_REQUIRE_EQUAL(log->first_block_num(), first);
BOOST_REQUIRE_EQUAL(eosio::chain::block_header::num_from_id(log->head_id()), last);
BOOST_REQUIRE(log->head_id());
BOOST_REQUIRE_EQUAL(eosio::chain::block_header::num_from_id(*log->head_id()), last);
if(enable_read) {
for(auto i = first; i <= last; i++) {
std::vector<char> buff;
+4 -2
View File
@@ -69,6 +69,7 @@ try:
Print("Wait for producing {} blocks".format(numBlocksToProduceBeforeRelaunch))
producingNode.waitForBlock(numBlocksToProduceBeforeRelaunch, blockType=BlockType.lib)
producingNode.waitForProducer("defproducera")
Print("Kill all node instances.")
for clusterNode in cluster.nodes:
@@ -83,8 +84,9 @@ try:
Utils.rmNodeDataDir(2)
Print ("Relaunch all cluster nodes instances.")
# -e -p eosio for resuming production, skipGenesis=False for launch the same chain as before
relaunchNode(producingNode, chainArg="-e -p eosio --sync-fetch-span 5 ", skipGenesis=False)
# -e for resuming production, defproducera only producer at this point
# skipGenesis=False for launch the same chain as before
relaunchNode(producingNode, chainArg="-e --sync-fetch-span 5 ", skipGenesis=False)
relaunchNode(speculativeNode1, chainArg="--sync-fetch-span 5 ")
relaunchNode(speculativeNode2, chainArg="--sync-fetch-span 5 ", skipGenesis=False)
+3 -1
View File
@@ -88,7 +88,8 @@ try:
Utils.Print("Bios node killed")
# need bios to pass along blocks so api node can continue without its other peer, but drop trx which is the point of this test
Utils.Print("Restart bios in drop transactions mode")
cluster.biosNode.relaunch("bios", addSwapFlags={"--p2p-accept-transactions": "false"})
if not cluster.biosNode.relaunch(addSwapFlags={"--p2p-accept-transactions": "false"}):
Utils.errorExit("Failed to relaunch bios node")
# *** create accounts to vote in desired producers ***
@@ -270,6 +271,7 @@ try:
if round % 3 == 0:
relaunchTime = time.perf_counter()
time.sleep(1) # give time for transactions to be sent
cluster.getNode(4).relaunch()
cluster.getNode(6).relaunch()
startRound = startRound - ( time.perf_counter() - relaunchTime )
+1 -1
View File
@@ -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,
@@ -131,7 +131,7 @@ class PerformanceTestBasic:
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})
+22 -2
View File
@@ -8,6 +8,9 @@ import unittest
import socket
import re
import shlex
import argparse
import sys
import signal
from pathlib import Path
from TestHarness import Account, Node, TestHelper, Utils, WalletMgr, ReturnType
@@ -68,7 +71,11 @@ class PluginHttpTest(unittest.TestCase):
shutil.rmtree(self.config_dir)
self.config_dir.mkdir()
# kill nodeos and keosd and clean up dirs
# kill nodeos. keosd shuts down automatically
def killNodes(self):
self.nodeos.kill(signal.SIGTERM)
# clean up dirs
def cleanEnv(self) :
if self.data_dir.exists():
shutil.rmtree(Utils.DataPath)
@@ -1536,11 +1543,24 @@ class PluginHttpTest(unittest.TestCase):
@classmethod
def tearDownClass(self):
self.cleanEnv(self)
global keepLogs
self.killNodes(self)
if unittest.TestResult().wasSuccessful() and not keepLogs:
self.cleanEnv(self)
if __name__ == "__main__":
test_category = True if os.environ.get("PLUGIN_HTTP_TEST_CATEGORY") == "ON" else False
category_config = HttpCategoryConfig(test_category)
parser = argparse.ArgumentParser()
parser.add_argument('--keep-logs', action='store_true')
parser.add_argument('unittest_args', nargs=argparse.REMAINDER)
args = parser.parse_args()
global keepLogs
keepLogs = args.keep_logs;
# Now set the sys.argv to the unittest_args (leaving sys.argv[0] alone)
sys.argv[1:] = args.unittest_args
unittest.main()
+26 -3
View File
@@ -22,7 +22,7 @@ errorExit=Utils.errorExit
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 @@ numTestRuns=args.num_test_runs
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 @@ testAccountName = "test"
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')
-1
View File
@@ -138,7 +138,6 @@ namespace eosio::testing {
if (resp_json.is_object() && resp_json.get_object().contains("processed")) {
const auto& processed = resp_json["processed"];
const auto& block_num = processed["block_num"].as_uint64();
const auto& transaction_id = processed["id"].as_string();
const auto& block_time = processed["block_time"].as_string();
std::string status = "failed";
uint32_t net = 0;
+21
View File
@@ -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)
{