Compare commits
57 Commits
v3.1.4
...
memory_fix
| Author | SHA1 | Date | |
|---|---|---|---|
| 95077ed429 | |||
| 05e6cc8c18 | |||
| 5caf5833ad | |||
| ed854f2bbd | |||
| e3976b8dc1 | |||
| 02c43ca656 | |||
| 1ec98976b1 | |||
| 93833b8e03 | |||
| d6cd2c9a7d | |||
| ae2679e164 | |||
| 0f25eaa33a | |||
| b7b4554a18 | |||
| 20e59a92bc | |||
| 31fd1361aa | |||
| 449f37133b | |||
| feeee5eb8f | |||
| 32de87b185 | |||
| 78dd785035 | |||
| f4b39ea29e | |||
| 20f45e5923 | |||
| fc2166a46d | |||
| 39a5ba846a | |||
| e4b28a7854 | |||
| e4819c35e1 | |||
| 7b7027cbee | |||
| 74fb1615d8 | |||
| 5523010cfd | |||
| 7c106605cc | |||
| 9047d1175b | |||
| da6a068639 | |||
| a886743005 | |||
| 0dbfb1a6ed | |||
| 9d1ea27ed7 | |||
| 71548a6425 | |||
| 72c5d299fe | |||
| 5cf31c9929 | |||
| 941b525093 | |||
| e377339f04 | |||
| fcbdb0ae98 | |||
| ecc5c4ec11 | |||
| caa01ca443 | |||
| 3e585d0f8a | |||
| 967ab869e0 | |||
| 0d977585ce | |||
| a357765c73 | |||
| 280d799958 | |||
| f694588f9f | |||
| 26025d7382 | |||
| 517ef20396 | |||
| 6f16383453 | |||
| debba62dd8 | |||
| 343596a0c1 | |||
| ec27d3d1f3 | |||
| f347ab534e | |||
| 3db765e0f1 | |||
| 0dbd5cf68c | |||
| da2a1dc996 |
@@ -0,0 +1,58 @@
|
||||
name: "Pinned Build"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
packages: read
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: Build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu18, ubuntu20, ubuntu22]
|
||||
runs-on: ["self-hosted", "enf-x86-beefy-long"]
|
||||
container: ${{ matrix.platform == 'ubuntu18' && 'ubuntu:bionic' || matrix.platform == 'ubuntu20' && 'ubuntu:focal' || 'ubuntu:jammy' }}
|
||||
steps:
|
||||
- name: Conditionally update git repo
|
||||
if: ${{ matrix.platform == 'ubuntu18' }}
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y software-properties-common
|
||||
apt-get update
|
||||
add-apt-repository ppa:git-core/ppa
|
||||
- name: Update and Install git
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git
|
||||
git --version
|
||||
- name: Clone leap
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# https://github.com/actions/runner/issues/2033
|
||||
chown -R $(id -u):$(id -g) $PWD
|
||||
./scripts/install_deps.sh
|
||||
- name: Build Pinned Build
|
||||
env:
|
||||
LEAP_PINNED_INSTALL_PREFIX: /usr
|
||||
run: |
|
||||
./scripts/pinned_build.sh deps build "$(nproc)"
|
||||
- name: Upload package
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: leap-${{matrix.platform}}-pinned-amd64
|
||||
path: build/leap-3*.deb
|
||||
- name: Run Parallel Tests
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure -j $(nproc) -LE "(nonparallelizable_tests|long_running_tests)" --timeout 420
|
||||
+1
-1
@@ -15,7 +15,7 @@ set( CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(VERSION_MAJOR 3)
|
||||
set(VERSION_MINOR 1)
|
||||
set(VERSION_PATCH 4)
|
||||
set(VERSION_PATCH 5)
|
||||
#set(VERSION_SUFFIX rc4)
|
||||
|
||||
if(VERSION_SUFFIX)
|
||||
|
||||
@@ -2125,7 +2125,7 @@ struct controller_impl {
|
||||
EOS_ASSERT( bsp, block_validate_exception, "null block" );
|
||||
const auto& b = bsp->block;
|
||||
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block < self.head_block_num()) {
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block <= self.head_block_num()) {
|
||||
ilog("Reached configured maximum block ${num}; terminating", ("num", conf.terminate_at_block) );
|
||||
shutdown();
|
||||
return;
|
||||
@@ -2160,7 +2160,7 @@ struct controller_impl {
|
||||
EOS_ASSERT( (s == controller::block_status::irreversible || s == controller::block_status::validated),
|
||||
block_validate_exception, "invalid block status for replay" );
|
||||
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block < self.head_block_num() ) {
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block <= self.head_block_num() ) {
|
||||
ilog("Reached configured maximum block ${num}; terminating", ("num", conf.terminate_at_block) );
|
||||
shutdown();
|
||||
return;
|
||||
@@ -3494,10 +3494,14 @@ std::optional<chain_id_type> controller::extract_chain_id_from_db( const path& s
|
||||
|
||||
if( db.revision() < 1 ) return {};
|
||||
|
||||
return db.get<global_property_object>().chain_id;
|
||||
} catch( const bad_database_version_exception& ) {
|
||||
throw;
|
||||
} catch( ... ) {
|
||||
auto * gpo = db.find<global_property_object>();
|
||||
if (gpo==nullptr) return {};
|
||||
|
||||
return gpo->chain_id;
|
||||
} catch( const std::system_error& e ) {
|
||||
// do not propagate db_error_code::not_found for absent db, so it will be created
|
||||
if( e.code().value() != chainbase::db_error_code::not_found )
|
||||
throw;
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace eosio { namespace chain {
|
||||
bool allow_ram_billing_in_notify = false;
|
||||
uint32_t maximum_variable_signature_length = chain::config::default_max_variable_signature_length;
|
||||
bool disable_all_subjective_mitigations = false; //< for developer & testing purposes, can be configured using `disable-all-subjective-mitigations` when `EOSIO_DEVELOPER` build option is provided
|
||||
uint32_t terminate_at_block = 0; //< primarily for testing purposes
|
||||
uint32_t terminate_at_block = 0;
|
||||
|
||||
wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime;
|
||||
eosvmoc::config eosvmoc_config;
|
||||
|
||||
@@ -247,7 +247,7 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_runtime<Impl>::instan
|
||||
wasm_code_ptr code((uint8_t*)code_bytes, code_size);
|
||||
apply_options options = { .max_pages = 65536,
|
||||
.max_call_depth = 0 };
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options);
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options, false); // uses 2-passes parsing
|
||||
eos_vm_host_functions_t::resolve(bkend->get_module());
|
||||
return std::make_unique<eos_vm_instantiated_module<Impl>>(this, std::move(bkend));
|
||||
} catch(eosio::vm::exception& e) {
|
||||
@@ -273,7 +273,7 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_profile_runtime::inst
|
||||
wasm_code_ptr code((uint8_t*)code_bytes, code_size);
|
||||
apply_options options = { .max_pages = 65536,
|
||||
.max_call_depth = 0 };
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options);
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options, false); // uses 2-passes parsing
|
||||
eos_vm_host_functions_t::resolve(bkend->get_module());
|
||||
return std::make_unique<eos_vm_profiling_module>(std::move(bkend), code_bytes, code_size);
|
||||
} catch(eosio::vm::exception& e) {
|
||||
|
||||
+1
-1
Submodule libraries/eos-vm updated: 7db4b33fa9...4da112c820
@@ -26,6 +26,7 @@
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <shared_mutex>
|
||||
|
||||
using namespace eosio::chain::plugin_interface;
|
||||
@@ -1776,7 +1777,6 @@ namespace eosio {
|
||||
if( c->is_transactions_only_connection() ) return;
|
||||
|
||||
uint32_t lib_num = 0;
|
||||
uint32_t peer_lib = msg.last_irreversible_block_num;
|
||||
uint32_t head = 0;
|
||||
block_id_type head_id;
|
||||
std::tie( lib_num, std::ignore, head,
|
||||
@@ -1790,10 +1790,10 @@ namespace eosio {
|
||||
peer_wlog(c, "Peer sent a handshake with a timestamp skewed by at least ${t}ms", ("t", network_latency_ns/1000000));
|
||||
network_latency_ns = 0;
|
||||
}
|
||||
// number of blocks syncing node is behind from a peer node
|
||||
uint32_t nblk_behind_by_net_latency = static_cast<uint32_t>(network_latency_ns / block_interval_ns);
|
||||
// 2x for time it takes for message to reach back to peer node, +1 to compensate for integer division truncation
|
||||
uint32_t nblk_combined_latency = 2 * nblk_behind_by_net_latency + 1;
|
||||
// number of blocks syncing node is behind from a peer node, round up
|
||||
uint32_t nblk_behind_by_net_latency = std::lround( static_cast<double>(network_latency_ns) / static_cast<double>(block_interval_ns) );
|
||||
// 2x for time it takes for message to reach back to peer node
|
||||
uint32_t nblk_combined_latency = 2 * nblk_behind_by_net_latency;
|
||||
// message in the log below is used in p2p_high_latency_test.py test
|
||||
peer_dlog(c, "Network latency is ${lat}ms, ${num} blocks discrepancy by network latency, ${tot_num} blocks discrepancy expected once message received",
|
||||
("lat", network_latency_ns/1000000)("num", nblk_behind_by_net_latency)("tot_num", nblk_combined_latency));
|
||||
@@ -1813,8 +1813,8 @@ namespace eosio {
|
||||
//-----------------------------
|
||||
|
||||
if (head_id == msg.head_id) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 0",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16)) );
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 0, lib ${l}",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16))("l", lib_num) );
|
||||
c->syncing = false;
|
||||
notice_message note;
|
||||
note.known_blocks.mode = none;
|
||||
@@ -1823,9 +1823,10 @@ namespace eosio {
|
||||
c->enqueue( note );
|
||||
return;
|
||||
}
|
||||
if (head < peer_lib) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 1",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16)) );
|
||||
if (head < msg.last_irreversible_block_num) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 1, head ${h}, lib ${l}",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16))
|
||||
("h", head)("l", lib_num) );
|
||||
c->syncing = false;
|
||||
if (c->sent_handshake_count > 0) {
|
||||
c->send_handshake();
|
||||
@@ -1833,8 +1834,9 @@ namespace eosio {
|
||||
return;
|
||||
}
|
||||
if (lib_num > msg.head_num + nblk_combined_latency) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 2",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16)) );
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 2, head ${h}, lib ${l}",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16))
|
||||
("h", head)("l", lib_num) );
|
||||
if (msg.generation > 1 || c->protocol_version > proto_base) {
|
||||
notice_message note;
|
||||
note.known_trx.pending = lib_num;
|
||||
@@ -1848,14 +1850,16 @@ namespace eosio {
|
||||
}
|
||||
|
||||
if (head + nblk_combined_latency < msg.head_num ) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 3",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16)) );
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 3, head ${h}, lib ${l}",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16))
|
||||
("h", head)("l", lib_num) );
|
||||
c->syncing = false;
|
||||
verify_catchup(c, msg.head_num, msg.head_id);
|
||||
return;
|
||||
} else if(head >= msg.head_num + nblk_combined_latency) {
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 4",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16)) );
|
||||
peer_ilog( c, "handshake lib ${lib}, head ${head}, head id ${id}.. sync 4, head ${h}, lib ${l}",
|
||||
("lib", msg.last_irreversible_block_num)("head", msg.head_num)("id", msg.head_id.str().substr(8,16))
|
||||
("h", head)("l", lib_num) );
|
||||
if (msg.generation > 1 || c->protocol_version > proto_base) {
|
||||
notice_message note;
|
||||
note.known_trx.mode = none;
|
||||
|
||||
@@ -41,6 +41,13 @@ using std::vector;
|
||||
using std::deque;
|
||||
using boost::signals2::scoped_connection;
|
||||
|
||||
// in release/3.2+ move to fc logger.hpp
|
||||
#define fc_tlog( LOGGER, FORMAT, ... ) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (LOGGER).is_enabled( fc::log_level::all ) ) \
|
||||
(LOGGER).log( FC_LOG_MESSAGE( all, FORMAT, __VA_ARGS__ ) ); \
|
||||
FC_MULTILINE_MACRO_END
|
||||
|
||||
#undef FC_LOG_AND_DROP
|
||||
#define LOG_AND_DROP() \
|
||||
catch ( const guard_exception& e ) { \
|
||||
@@ -197,8 +204,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void set_max_failures_per_account( uint32_t max_failures ) {
|
||||
max_failures_per_account = max_failures;
|
||||
void set_max_failures_per_account( uint32_t max_failures, uint32_t size ) {
|
||||
max_failures_per_account = max_failures;
|
||||
reset_window_size_in_num_blocks = size;
|
||||
}
|
||||
|
||||
void add( const account_name& n, int64_t exception_code ) {
|
||||
@@ -218,10 +226,24 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
void report() const {
|
||||
if( _log.is_enabled( fc::log_level::debug ) ) {
|
||||
void report_and_clear(uint32_t block_num) {
|
||||
if (last_reset_block_num != block_num && (block_num % reset_window_size_in_num_blocks == 0) ) {
|
||||
report(block_num);
|
||||
failed_accounts.clear();
|
||||
last_reset_block_num = block_num;
|
||||
}
|
||||
}
|
||||
|
||||
fc::time_point next_reset_timepoint(uint32_t current_block_num, fc::time_point current_block_time) const {
|
||||
auto num_blocks_to_reset = reset_window_size_in_num_blocks - (current_block_num % reset_window_size_in_num_blocks);
|
||||
return current_block_time + fc::milliseconds(num_blocks_to_reset * eosio::chain::config::block_interval_ms);
|
||||
}
|
||||
|
||||
private:
|
||||
void report(uint32_t block_num) const {
|
||||
if( _log.is_enabled(fc::log_level::debug)) {
|
||||
auto now = fc::time_point::now();
|
||||
for( const auto& e : failed_accounts ) {
|
||||
for ( const auto& e : failed_accounts ) {
|
||||
std::string reason;
|
||||
if( e.second.is_deadline() ) reason += "deadline";
|
||||
if( e.second.is_tx_cpu_usage() ) {
|
||||
@@ -242,12 +264,6 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
failed_accounts.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
struct account_failure {
|
||||
enum class ex_fields : uint8_t {
|
||||
ex_deadline_exception = 1,
|
||||
@@ -282,6 +298,8 @@ private:
|
||||
|
||||
std::map<account_name, account_failure> failed_accounts;
|
||||
uint32_t max_failures_per_account = 3;
|
||||
uint32_t last_reset_block_num = 0;
|
||||
uint32_t reset_window_size_in_num_blocks = 1;
|
||||
const eosio::subjective_billing& subjective_billing;
|
||||
};
|
||||
|
||||
@@ -675,8 +693,9 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin
|
||||
auto first_auth = trx->packed_trx()->get_transaction().first_authorizer();
|
||||
if( _account_fails.failure_limit( first_auth ) ) {
|
||||
send_response( std::static_pointer_cast<fc::exception>( std::make_shared<tx_cpu_usage_exceeded>(
|
||||
FC_LOG_MESSAGE( error, "transaction ${id} exceeded failure limit for account ${a}",
|
||||
("id", trx->id())("a", first_auth) ) ) ) );
|
||||
FC_LOG_MESSAGE( error, "transaction ${id} exceeded failure limit for account ${a} until ${next_reset_time}",
|
||||
("id", trx->id())("a", first_auth)
|
||||
("next_reset_time", _account_fails.next_reset_timepoint(chain.head_block_num(),chain.head_block_time())) ) ) ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -696,7 +715,7 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin
|
||||
|
||||
auto prev_billed_cpu_time_us = trx->billed_cpu_time_us;
|
||||
auto trace = chain.push_transaction( trx, block_deadline, max_trx_time, prev_billed_cpu_time_us, false, sub_bill );
|
||||
fc_dlog( _trx_failed_trace_log, "Subjective bill for ${a}: ${b} elapsed ${t}us", ("a",first_auth)("b",sub_bill)("t",trace->elapsed));
|
||||
fc_tlog( _log, "Subjective bill for ${a}: ${b} elapsed ${t}us", ("a",first_auth)("b",sub_bill)("t",trace->elapsed));
|
||||
if( trace->except ) {
|
||||
if( exception_is_exhausted( *trace->except ) ) {
|
||||
_unapplied_transactions.add_incoming( trx, persist_until_expired, return_failure_traces, next );
|
||||
@@ -879,7 +898,9 @@ void producer_plugin::set_program_options(
|
||||
("subjective-cpu-leeway-us", boost::program_options::value<int32_t>()->default_value( config::default_subjective_cpu_leeway_us ),
|
||||
"Time in microseconds allowed for a transaction that starts with insufficient CPU quota to complete and cover its CPU usage.")
|
||||
("subjective-account-max-failures", boost::program_options::value<uint32_t>()->default_value(3),
|
||||
"Sets the maximum amount of failures that are allowed for a given account per block.")
|
||||
"Sets the maximum amount of failures that are allowed for a given account per window size.")
|
||||
("subjective-account-max-failures-window-size", boost::program_options::value<uint32_t>()->default_value(1),
|
||||
"Sets the window size in number of blocks for subjective-account-max-failures.")
|
||||
("subjective-account-decay-time-minutes", bpo::value<uint32_t>()->default_value( config::account_cpu_usage_average_window_ms / 1000 / 60 ),
|
||||
"Sets the time to return full subjective cpu for accounts")
|
||||
("incoming-defer-ratio", bpo::value<double>()->default_value(1.0),
|
||||
@@ -1029,7 +1050,13 @@ void producer_plugin::plugin_initialize(const boost::program_options::variables_
|
||||
|
||||
my->_keosd_provider_timeout_us = fc::milliseconds(options.at("keosd-provider-timeout").as<int32_t>());
|
||||
|
||||
my->_account_fails.set_max_failures_per_account( options.at("subjective-account-max-failures").as<uint32_t>() );
|
||||
auto subjective_account_max_failures_window_size = options.at("subjective-account-max-failures-window-size").as<uint32_t>();
|
||||
EOS_ASSERT( subjective_account_max_failures_window_size > 0, plugin_config_exception,
|
||||
"subjective-account-max-failures-window-size ${s} must be greater than 0", ("s", subjective_account_max_failures_window_size) );
|
||||
|
||||
my->_account_fails.set_max_failures_per_account( options.at("subjective-account-max-failures").as<uint32_t>(),
|
||||
subjective_account_max_failures_window_size );
|
||||
|
||||
|
||||
my->_produce_time_offset_us = options.at("produce-time-offset-us").as<int32_t>();
|
||||
EOS_ASSERT( my->_produce_time_offset_us <= 0 && my->_produce_time_offset_us >= -config::block_interval_us, plugin_config_exception,
|
||||
@@ -1685,7 +1712,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
|
||||
|
||||
const auto& hbs = chain.head_block_state();
|
||||
|
||||
if( chain.get_terminate_at_block() > 0 && chain.get_terminate_at_block() < chain.head_block_num() ) {
|
||||
if( chain.get_terminate_at_block() > 0 && chain.get_terminate_at_block() <= chain.head_block_num() ) {
|
||||
ilog("Reached configured maximum block ${num}; terminating", ("num", chain.get_terminate_at_block()));
|
||||
app().quit();
|
||||
return start_block_result::failed;
|
||||
@@ -1852,7 +1879,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
|
||||
}
|
||||
|
||||
try {
|
||||
_account_fails.clear();
|
||||
_account_fails.report_and_clear(hbs->block_num);
|
||||
|
||||
if( !remove_expired_trxs( preprocess_deadline ) )
|
||||
return start_block_result::exhausted;
|
||||
@@ -2025,8 +2052,9 @@ bool producer_plugin_impl::process_unapplied_trxs( const fc::time_point& deadlin
|
||||
++num_failed;
|
||||
if( itr->next ) {
|
||||
itr->next( std::make_shared<tx_cpu_usage_exceeded>(
|
||||
FC_LOG_MESSAGE( error, "transaction ${id} exceeded failure limit for account ${a}",
|
||||
("id", trx->id())("a", first_auth) ) ) );
|
||||
FC_LOG_MESSAGE( error, "transaction ${id} exceeded failure limit for account ${a} until ${next_reset_time}",
|
||||
("id", trx->id())("a", first_auth)
|
||||
("next_reset_time", _account_fails.next_reset_timepoint(chain.head_block_num(),chain.head_block_time())) ) ) );
|
||||
}
|
||||
itr = _unapplied_transactions.erase( itr );
|
||||
continue;
|
||||
@@ -2049,7 +2077,7 @@ bool producer_plugin_impl::process_unapplied_trxs( const fc::time_point& deadlin
|
||||
|| trx->read_only;
|
||||
|
||||
auto trace = chain.push_transaction( trx, deadline, max_trx_time, prev_billed_cpu_time_us, false, sub_bill );
|
||||
fc_dlog( _trx_failed_trace_log, "Subjective unapplied bill for ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
fc_tlog( _log, "Subjective unapplied bill for ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
if( trace->except ) {
|
||||
if( exception_is_exhausted( *trace->except ) ) {
|
||||
if( block_is_exhausted() ) {
|
||||
@@ -2058,7 +2086,7 @@ bool producer_plugin_impl::process_unapplied_trxs( const fc::time_point& deadlin
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fc_dlog( _trx_failed_trace_log, "Subjective unapplied bill for failed ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
fc_tlog( _log, "Subjective unapplied bill for failed ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
auto failure_code = trace->except->code();
|
||||
if( failure_code != tx_duplicate::code_value ) {
|
||||
// this failed our configured maximum transaction time, we don't want to replay it
|
||||
@@ -2077,7 +2105,7 @@ bool producer_plugin_impl::process_unapplied_trxs( const fc::time_point& deadlin
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
fc_dlog( _trx_successful_trace_log, "Subjective unapplied bill for success ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
fc_tlog( _log, "Subjective unapplied bill for success ${a}: ${b} prev ${t}us", ("a",first_auth)("b",prev_billed_cpu_time_us)("t",trace->elapsed));
|
||||
// if db_read_mode SPECULATIVE then trx is in the pending block and not immediately reverted
|
||||
if (!disable_subjective_billing)
|
||||
_subjective_billing.subjective_bill( trx->id(), trx->packed_trx()->expiration(), first_auth, trace->elapsed,
|
||||
@@ -2439,9 +2467,6 @@ void producer_plugin_impl::produce_block() {
|
||||
|
||||
block_state_ptr new_bs = chain.head_block_state();
|
||||
|
||||
_account_fails.report();
|
||||
_account_fails.clear();
|
||||
|
||||
ilog("Produced block ${id}... #${n} @ ${t} signed by ${p} [trxs: ${count}, lib: ${lib}, confirmed: ${confs}]",
|
||||
("p",new_bs->header.producer)("id",new_bs->id.str().substr(8,16))
|
||||
("n",new_bs->block_num)("t",new_bs->header.timestamp)
|
||||
|
||||
@@ -71,6 +71,17 @@ bfs::path determine_home_directory()
|
||||
return home;
|
||||
}
|
||||
|
||||
enum return_codes {
|
||||
OTHER_FAIL = -2,
|
||||
INITIALIZE_FAIL = -1,
|
||||
SUCCESS = 0,
|
||||
BAD_ALLOC = 1,
|
||||
DATABASE_DIRTY = 2,
|
||||
FIXED_REVERSIBLE = SUCCESS,
|
||||
EXTRACTED_GENESIS = SUCCESS,
|
||||
NODE_MANAGEMENT_SUCCESS = 5
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try {
|
||||
@@ -90,6 +101,7 @@ int main(int argc, char** argv)
|
||||
opts.count("print-default-config")) {
|
||||
return 0;
|
||||
}
|
||||
return INITIALIZE_FAIL;
|
||||
}
|
||||
initialize_logging();
|
||||
auto& http = app().get_plugin<http_plugin>();
|
||||
|
||||
@@ -132,7 +132,7 @@ pushdir ${LEAP_DIR}
|
||||
|
||||
# build Leap
|
||||
echo "Building Leap ${SCRIPT_DIR}"
|
||||
try cmake -DCMAKE_TOOLCHAIN_FILE=${SCRIPT_DIR}/pinned_toolchain.cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=${LLVM_DIR}/lib/cmake -DCMAKE_PREFIX_PATH=${BOOST_DIR}/bin ${SCRIPT_DIR}/..
|
||||
try cmake -DCMAKE_TOOLCHAIN_FILE=${SCRIPT_DIR}/pinned_toolchain.cmake -DCMAKE_INSTALL_PREFIX=${LEAP_PINNED_INSTALL_PREFIX:-/usr/local} -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=${LLVM_DIR}/lib/cmake -DCMAKE_PREFIX_PATH=${BOOST_DIR}/bin ${SCRIPT_DIR}/..
|
||||
|
||||
try make -j${JOBS}
|
||||
try cpack
|
||||
|
||||
@@ -120,11 +120,11 @@ add_test(NAME restart-scenarios-test-hard_replay COMMAND tests/restart-scenarios
|
||||
set_property(TEST restart-scenarios-test-hard_replay PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME restart-scenarios-test-none COMMAND tests/restart-scenarios-test.py -c none --kill-sig term -p4 -v --clean-run --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST restart-scenarios-test-none PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME terminate-scenarios-test-resync COMMAND tests/terminate-scenarios-test.py -p 1 -c resync --kill-count 1 --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
add_test(NAME terminate-scenarios-test-resync COMMAND tests/terminate-scenarios-test.py -c resync --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST terminate-scenarios-test-resync PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME terminate-scenarios-test-replay COMMAND tests/terminate-scenarios-test.py -p 1 -c replay --kill-count 1 --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
add_test(NAME terminate-scenarios-test-replay COMMAND tests/terminate-scenarios-test.py -c replay --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST terminate-scenarios-test-replay PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME terminate-scenarios-test-hard_replay COMMAND tests/terminate-scenarios-test.py -p 1 -c hardReplay --kill-count 1 --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
add_test(NAME terminate-scenarios-test-hard_replay COMMAND tests/terminate-scenarios-test.py -c hardReplay --terminate-at-block 10 --kill-sig term --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST terminate-scenarios-test-hard_replay PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME validate_dirty_db_test COMMAND tests/validate-dirty-db.py -v --clean-run --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST validate_dirty_db_test PROPERTY LABELS nonparallelizable_tests)
|
||||
|
||||
+2
-2
@@ -1430,14 +1430,14 @@ class Cluster(object):
|
||||
time.sleep(1) # Give processes time to stand down
|
||||
return True
|
||||
|
||||
def relaunchEosInstances(self, cachePopen=False, nodeArgs=""):
|
||||
def relaunchEosInstances(self, cachePopen=False, nodeArgs="", waitForTerm=False):
|
||||
|
||||
chainArg=self.__chainSyncStrategy.arg + " " + nodeArgs
|
||||
|
||||
newChain= False if self.__chainSyncStrategy.name in [Utils.SyncHardReplayTag, Utils.SyncNoneTag] else True
|
||||
for i in range(0, len(self.nodes)):
|
||||
node=self.nodes[i]
|
||||
if node.killed and not node.relaunch(chainArg, newChain=newChain, cachePopen=cachePopen):
|
||||
if node.killed and not node.relaunch(chainArg, newChain=newChain, cachePopen=cachePopen, waitForTerm=waitForTerm):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
+6
-5
@@ -1210,7 +1210,7 @@ class Node(object):
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
# If nodeosPath is equal to None, it will use the existing nodeos path
|
||||
def relaunch(self, chainArg=None, newChain=False, skipGenesis=True, timeout=Utils.systemWaitTimeout, addSwapFlags=None, cachePopen=False, nodeosPath=None, waitForTerm=True):
|
||||
def relaunch(self, chainArg=None, newChain=False, skipGenesis=True, timeout=Utils.systemWaitTimeout, addSwapFlags=None, cachePopen=False, nodeosPath=None, waitForTerm=False):
|
||||
|
||||
assert(self.pid is None)
|
||||
assert(self.killed)
|
||||
@@ -1265,15 +1265,16 @@ class Node(object):
|
||||
except subprocess.TimeoutExpired:
|
||||
return False
|
||||
with open(popen.errfile.name, 'r') as f:
|
||||
if "Reached configured maximum block 10; terminating" in f.read():
|
||||
if "successfully exiting" in f.read():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if "terminate-at-block" not in cmd or not waitForTerm:
|
||||
isAlive=Utils.waitForBool(isNodeAlive, timeout, sleepTime=1)
|
||||
else:
|
||||
if waitForTerm:
|
||||
isAlive=Utils.waitForBoolWithArg(didNodeExitGracefully, self.popenProc, timeout, sleepTime=1)
|
||||
else:
|
||||
isAlive=Utils.waitForBool(isNodeAlive, timeout, sleepTime=1)
|
||||
|
||||
if isAlive:
|
||||
Utils.Print("Node relaunch was successful.")
|
||||
else:
|
||||
|
||||
@@ -375,6 +375,8 @@ try:
|
||||
if preKillBlockProducer == "defproducerj" or preKillBlockProducer == "defproducerk":
|
||||
# wait for defproduceri so there is plenty of time to send kill before defproducerk
|
||||
nonProdNode.waitForProducer("defproduceri")
|
||||
preKillBlockNum=nonProdNode.getBlockNum()
|
||||
preKillBlockProducer=nonProdNode.getBlockProducerByNum(preKillBlockNum)
|
||||
Print("preKillBlockProducer = {}".format(preKillBlockProducer))
|
||||
# kill at last block before defproducerl, since the block it is killed on will get propagated
|
||||
killAtProducer="defproducerk"
|
||||
|
||||
@@ -25,8 +25,6 @@ relaunchTimeout = 10
|
||||
numOfProducers = 1
|
||||
totalNodes = 4
|
||||
|
||||
termAtFutureBlockNum = 30
|
||||
|
||||
# Parse command line arguments
|
||||
args = TestHelper.parse_args({
|
||||
"-v",
|
||||
@@ -47,7 +45,7 @@ keepLogs = args.keep_logs
|
||||
# Wrapper function to execute test
|
||||
# This wrapper function will resurrect the node to be tested, and shut
|
||||
# it down by the end of the test
|
||||
def executeTest(cluster, producerNode, testNodeId, testNodeArgs, resultMsgs):
|
||||
def executeTest(cluster, testNodeId, testNodeArgs, resultMsgs):
|
||||
testNode = None
|
||||
testResult = False
|
||||
resultDesc = "!!!BUG IS CONFIRMED ON TEST CASE #{} ({})".format(
|
||||
@@ -63,17 +61,12 @@ def executeTest(cluster, producerNode, testNodeId, testNodeArgs, resultMsgs):
|
||||
)
|
||||
)
|
||||
|
||||
# Get current information from the producer.
|
||||
producerNode.waitForHeadToAdvance()
|
||||
|
||||
# Launch the node with the terminate-at-block option specified
|
||||
# in testNodeArgs. The option for each node has already
|
||||
# been set via specificExtraNodeosArg in the cluster launch.
|
||||
cluster.launchUnstarted(cachePopen=True)
|
||||
testNode = cluster.getNode(testNodeId)
|
||||
assert not testNode.verifyAlive() # resets pid so reluanch works
|
||||
testNode.relaunch(addSwapFlags={"--terminate-at-block": "9999999"}, cachePopen=True)
|
||||
|
||||
# Wait for node to start up.
|
||||
time.sleep(10)
|
||||
time.sleep(3)
|
||||
|
||||
# Check the node stops at the correct block.
|
||||
checkStatus(testNode, testNodeArgs)
|
||||
@@ -107,8 +100,7 @@ def checkStatus(testNode, testNodeArgs):
|
||||
"""Test --terminate-at-block stops at the correct block."""
|
||||
Print(" ".join([
|
||||
"The test node has begun receiving from the producing node and",
|
||||
"is expected to stop at or little bigger than the block number",
|
||||
"specified here: ",
|
||||
"is expected to stop at the block number specified here: ",
|
||||
testNodeArgs
|
||||
]))
|
||||
|
||||
@@ -122,6 +114,12 @@ def checkStatus(testNode, testNodeArgs):
|
||||
else:
|
||||
checkHeadOrSpeculative(head, lib)
|
||||
|
||||
# Check for the terminate at block message.
|
||||
match = re.search(r"--terminate-at-block (\d+)", testNodeArgs)
|
||||
termAtBlock = int(match.group(1))
|
||||
|
||||
assert head == termAtBlock, f"head {head} termAtBlock {termAtBlock}"
|
||||
|
||||
|
||||
def checkReplay(testNode, testNodeArgs):
|
||||
"""Test --terminate-at-block with --replay-blockchain."""
|
||||
@@ -135,20 +133,18 @@ def checkReplay(testNode, testNodeArgs):
|
||||
testNodeArgs
|
||||
]))
|
||||
|
||||
testNode.relaunch(chainArg="--replay-blockchain", cachePopen=True)
|
||||
assert not testNode.verifyAlive()
|
||||
testNode.relaunch(chainArg="--replay-blockchain", addSwapFlags={"--terminate-at-block": "9999999"}, cachePopen=True)
|
||||
|
||||
# Wait for node to finish up.
|
||||
time.sleep(10)
|
||||
time.sleep(3)
|
||||
|
||||
# Check for the terminate at block message.
|
||||
match = re.search(r"--terminate-at-block=(\d+)", testNodeArgs)
|
||||
match = re.search(r"--terminate-at-block (\d+)", testNodeArgs)
|
||||
termAtBlock = int(match.group(1))
|
||||
|
||||
termMsg = "Reached configured maximum block {}; terminating".format(
|
||||
termAtBlock
|
||||
)
|
||||
|
||||
assert checkLog(testNode.popenProc.errfile.name, termMsg)
|
||||
head, lib = getBlockNumInfo(testNode)
|
||||
assert head == termAtBlock, f"head {head} termAtBlock {termAtBlock}"
|
||||
|
||||
|
||||
def getBlockNumInfo(testNode):
|
||||
@@ -159,11 +155,12 @@ def getBlockNumInfo(testNode):
|
||||
info = testNode.getInfo()
|
||||
|
||||
if not info:
|
||||
break
|
||||
continue
|
||||
|
||||
try:
|
||||
head = info["head_block_num"]
|
||||
lib = info["last_irreversible_block_num"]
|
||||
break
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -172,26 +169,6 @@ def getBlockNumInfo(testNode):
|
||||
return head, lib
|
||||
|
||||
|
||||
def checkLog(log, message, sleepDuration=1, maxAttempt=10):
|
||||
attemptCnt = 0
|
||||
found = False
|
||||
|
||||
while not found and attemptCnt < maxAttempt:
|
||||
try:
|
||||
with open(log, "r") as f:
|
||||
found = message in f.read()
|
||||
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if not found:
|
||||
time.sleep(sleepDuration)
|
||||
|
||||
attemptCnt = attemptCnt + 1
|
||||
|
||||
return found
|
||||
|
||||
|
||||
def checkIrreversible(head, lib):
|
||||
assert head == lib, (
|
||||
"Head ({}) should be equal to lib ({})".format(head, lib)
|
||||
@@ -215,9 +192,9 @@ testSuccessful = False
|
||||
try:
|
||||
specificNodeosArgs = {
|
||||
0 : "--enable-stale-production",
|
||||
1 : "--read-mode irreversible --terminate-at-block=250",
|
||||
2 : "--read-mode speculative --terminate-at-block=550",
|
||||
3 : "--read-mode head --terminate-at-block=850",
|
||||
1 : "--read-mode irreversible --terminate-at-block 75",
|
||||
2 : "--read-mode speculative --terminate-at-block 100",
|
||||
3 : "--read-mode head --terminate-at-block 125",
|
||||
}
|
||||
traceNodeosArgs = " --plugin eosio::trace_api_plugin --trace-no-abis "
|
||||
|
||||
@@ -229,7 +206,6 @@ try:
|
||||
prodCount=numOfProducers,
|
||||
totalProducers=numOfProducers,
|
||||
totalNodes=totalNodes,
|
||||
unstartedNodes=totalNodes - numOfProducers,
|
||||
pnodes=1,
|
||||
useBiosBootFile=False,
|
||||
topo="mesh",
|
||||
@@ -240,6 +216,11 @@ try:
|
||||
producingNodeId = 0
|
||||
producingNode = cluster.getNode(producingNodeId)
|
||||
|
||||
# wait for all to terminate, needs to be larger than largest terminate-at-block
|
||||
producingNode.waitForBlock( 150, timeout=150 )
|
||||
cluster.biosNode.kill(signal.SIGTERM)
|
||||
producingNode.kill(signal.SIGTERM)
|
||||
|
||||
# Start executing test cases here
|
||||
Utils.Print("Script Begin .............................")
|
||||
|
||||
@@ -250,7 +231,6 @@ try:
|
||||
|
||||
success = executeTest(
|
||||
cluster,
|
||||
producingNode,
|
||||
nodeId,
|
||||
nodeArgs,
|
||||
testResultMsgs
|
||||
|
||||
@@ -20,16 +20,15 @@ import random
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
args=TestHelper.parse_args({"-p","-d","-s","-c","--kill-sig","--kill-count","--keep-logs"
|
||||
args=TestHelper.parse_args({"-d","-s","-c","--kill-sig","--keep-logs"
|
||||
,"--dump-error-details","-v","--leave-running","--clean-run"
|
||||
,"--terminate-at-block"})
|
||||
pnodes=args.p
|
||||
pnodes=1
|
||||
topo=args.s
|
||||
delay=args.d
|
||||
chainSyncStrategyStr=args.c
|
||||
debug=args.v
|
||||
total_nodes = pnodes
|
||||
killCount=args.kill_count if args.kill_count > 0 else 1
|
||||
killSignal=args.kill_sig
|
||||
killEosInstances= not args.leave_running
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
@@ -70,19 +69,20 @@ try:
|
||||
if not cluster.waitOnClusterBlockNumSync(3):
|
||||
errorExit("Cluster never stabilized")
|
||||
|
||||
Print("Kill %d cluster node instances." % (killCount))
|
||||
if cluster.killSomeEosInstances(killCount, killSignal) is False:
|
||||
Print("Kill cluster node instance.")
|
||||
if cluster.killSomeEosInstances(1, killSignal) is False:
|
||||
errorExit("Failed to kill Eos instances")
|
||||
assert not cluster.getNode(0).verifyAlive()
|
||||
Print("nodeos instances killed.")
|
||||
|
||||
Print ("Relaunch dead cluster nodes instances.")
|
||||
Print ("Relaunch dead cluster node instance.")
|
||||
nodeArg = "--terminate-at-block %d" % terminate if terminate > 0 else ""
|
||||
if nodeArg != "":
|
||||
if chainSyncStrategyStr == "hardReplay":
|
||||
nodeArg += " --truncate-at-block %d" % terminate
|
||||
if cluster.relaunchEosInstances(cachePopen=True, nodeArgs=nodeArg) is False:
|
||||
errorExit("Failed to relaunch Eos instances")
|
||||
Print("nodeos instances relaunched.")
|
||||
if cluster.relaunchEosInstances(cachePopen=True, nodeArgs=nodeArg, waitForTerm=(terminate > 0)) is False:
|
||||
errorExit("Failed to relaunch Eos instance")
|
||||
Print("nodeos instance relaunched.")
|
||||
|
||||
testSuccessful=True
|
||||
finally:
|
||||
|
||||
@@ -332,9 +332,9 @@ def stepSetSystemContract():
|
||||
retry(args.cleos + 'push action eosio activate \'["ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43"]\' -p eosio@active')
|
||||
# DISALLOW_EMPTY_PRODUCER_SCHEDULE
|
||||
retry(args.cleos + 'push action eosio activate \'["68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428"]\' -p eosio@active')
|
||||
# FIX_LINKAUTH_RESTRICTION
|
||||
# FIX_LINKAUTH_RESTRICTION
|
||||
retry(args.cleos + 'push action eosio activate \'["e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526"]\' -p eosio@active')
|
||||
# REPLACE_DEFERRED
|
||||
# REPLACE_DEFERRED
|
||||
retry(args.cleos + 'push action eosio activate \'["ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99"]\' -p eosio@active')
|
||||
# NO_DUPLICATE_DEFERRED_ID
|
||||
retry(args.cleos + 'push action eosio activate \'["4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f"]\' -p eosio@active')
|
||||
@@ -346,6 +346,12 @@ def stepSetSystemContract():
|
||||
retry(args.cleos + 'push action eosio activate \'["4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2"]\' -p eosio@active')
|
||||
# WTMSIG_BLOCK_SIGNATURES
|
||||
retry(args.cleos + 'push action eosio activate \'["299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707"]\' -p eosio@active')
|
||||
# GET_CODE_HASH
|
||||
retry(args.cleos + 'push action eosio activate \'["bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99"]\' -p eosio@active')
|
||||
# GET_BLOCK_NUM
|
||||
retry(args.cleos + 'push action eosio activate \'["35c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b"]\' -p eosio@active')
|
||||
# CRYPTO_PRIMITIVES
|
||||
retry(args.cleos + 'push action eosio activate \'["6bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc"]\' -p eosio@active')
|
||||
sleep(1)
|
||||
|
||||
# install eosio.system latest version
|
||||
|
||||
Reference in New Issue
Block a user