Merge branch 'main' of github.com:AntelopeIO/leap into gh-1189-test
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) );
|
||||
|
||||
@@ -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>>();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user