diff --git a/CMakeLists.txt b/CMakeLists.txt index cbc2123d351..a767c0f3abc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ set( CXX_STANDARD_REQUIRED ON) set(VERSION_MAJOR 1) set(VERSION_MINOR 1) -set(VERSION_PATCH 2) +set(VERSION_PATCH 3) set( CLI_CLIENT_EXECUTABLE_NAME cleos ) set( GUI_CLIENT_EXECUTABLE_NAME eosio ) diff --git a/Docker/README.md b/Docker/README.md index faac50468e5..ff1730ec034 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -20,10 +20,10 @@ cd eos/Docker docker build . -t eosio/eos ``` -The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.1.2 tag, you could do the following: +The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.1.3 tag, you could do the following: ```bash -docker build -t eosio/eos:v1.1.2 --build-arg branch=v1.1.2 . +docker build -t eosio/eos:v1.1.3 --build-arg branch=v1.1.3 . ``` By default, the symbol in eosio.system is set to SYS. You can override this using the symbol argument while building the docker image. diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 482fbc7b730..87a91949c80 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -467,7 +467,9 @@ struct controller_impl { transaction_trace_ptr apply_onerror( const generated_transaction& gtrx, fc::time_point deadline, fc::time_point start, - uint32_t billed_cpu_time_us) { + uint32_t& cpu_time_to_bill_us, // only set on failure + uint32_t billed_cpu_time_us, + bool explicit_billed_cpu_time = false ) { signed_transaction etrx; // Deliver onerror action containing the failed deferred transaction directly back to the sender. etrx.actions.emplace_back( vector{{gtrx.sender, config::active_name}}, @@ -477,6 +479,7 @@ struct controller_impl { transaction_context trx_context( self, etrx, etrx.id(), start ); trx_context.deadline = deadline; + trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time; trx_context.billed_cpu_time_us = billed_cpu_time_us; transaction_trace_ptr trace = trx_context.trace; try { @@ -497,6 +500,7 @@ struct controller_impl { restore.cancel(); return trace; } catch( const fc::exception& e ) { + cpu_time_to_bill_us = trx_context.update_billed_cpu_time( fc::time_point::now() ); trace->except = e; trace->except_ptr = std::current_exception(); } @@ -529,14 +533,14 @@ struct controller_impl { || (code == key_blacklist_exception::code_value); } - transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us ) { + transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false ) { const auto& idx = db.get_index(); auto itr = idx.find( trxid ); EOS_ASSERT( itr != idx.end(), unknown_transaction_exception, "unknown transaction" ); - return push_scheduled_transaction( *itr, deadline, billed_cpu_time_us ); + return push_scheduled_transaction( *itr, deadline, billed_cpu_time_us, explicit_billed_cpu_time ); } - transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline, uint32_t billed_cpu_time_us ) + transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline, uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false ) { try { auto undo_session = db.start_undo_session(true); auto gtrx = generated_transaction(gto); @@ -572,15 +576,16 @@ struct controller_impl { }); in_trx_requiring_checks = true; + uint32_t cpu_time_to_bill_us = billed_cpu_time_us; + transaction_context trx_context( self, dtrx, gtrx.trx_id ); - trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource + trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource trx_context.deadline = deadline; + trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time; trx_context.billed_cpu_time_us = billed_cpu_time_us; transaction_trace_ptr trace = trx_context.trace; - flat_set bill_to_accounts; try { trx_context.init_for_deferred_trx( gtrx.published ); - bill_to_accounts = trx_context.bill_to_accounts; trx_context.exec(); trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful @@ -600,35 +605,48 @@ struct controller_impl { restore.cancel(); return trace; } catch( const fc::exception& e ) { + cpu_time_to_bill_us = trx_context.update_billed_cpu_time( fc::time_point::now() ); trace->except = e; trace->except_ptr = std::current_exception(); trace->elapsed = fc::time_point::now() - trx_context.start; } trx_context.undo_session.undo(); - // Only soft or hard failure logic below: + // Only subjective OR soft OR hard failure logic below: if( gtrx.sender != account_name() && !failure_is_subjective(*trace->except)) { // Attempt error handling for the generated transaction. dlog("${detail}", ("detail", trace->except->to_detail_string())); - auto error_trace = apply_onerror( gtrx, deadline, trx_context.start, trx_context.billed_cpu_time_us ); + auto error_trace = apply_onerror( gtrx, deadline, trx_context.pseudo_start, cpu_time_to_bill_us, billed_cpu_time_us, explicit_billed_cpu_time ); error_trace->failed_dtrx_trace = trace; trace = error_trace; if( !trace->except_ptr ) { undo_session.squash(); return trace; } + trace->elapsed = fc::time_point::now() - trx_context.start; } - // Only hard failure OR subjective failure logic below: + // Only subjective OR hard failure logic below: - trace->elapsed = fc::time_point::now() - trx_context.start; + if (!failure_is_subjective(*trace->except)) { + // hard failure logic - resource_limits.add_transaction_usage( bill_to_accounts, trx_context.billed_cpu_time_us, 0, - block_timestamp_type(self.pending_block_time()).slot ); // Should never fail + if( !explicit_billed_cpu_time ) { + auto& rl = self.get_mutable_resource_limits_manager(); + rl.update_account_usage( trx_context.bill_to_accounts, block_timestamp_type(self.pending_block_time()).slot ); + int64_t account_cpu_limit = 0; + std::tie( std::ignore, account_cpu_limit, std::ignore ) = trx_context.max_bandwidth_billed_accounts_can_pay( true ); - if (!failure_is_subjective(*trace->except)) { - trace->receipt = push_receipt(gtrx.trx_id, transaction_receipt::hard_fail, trx_context.billed_cpu_time_us, 0); + cpu_time_to_bill_us = static_cast( std::min( std::min( static_cast(cpu_time_to_bill_us), + account_cpu_limit ), + trx_context.initial_objective_duration_limit.count() ) ); + } + + resource_limits.add_transaction_usage( trx_context.bill_to_accounts, cpu_time_to_bill_us, 0, + block_timestamp_type(self.pending_block_time()).slot ); // Should never fail + + trace->receipt = push_receipt(gtrx.trx_id, transaction_receipt::hard_fail, cpu_time_to_bill_us, 0); emit( self.applied_transaction, trace ); undo_session.squash(); } @@ -661,7 +679,8 @@ struct controller_impl { transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline, bool implicit, - uint32_t billed_cpu_time_us) + uint32_t billed_cpu_time_us, + bool explicit_billed_cpu_time = false ) { EOS_ASSERT(deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized"); @@ -672,6 +691,7 @@ struct controller_impl { trx_context.leeway = *subjective_cpu_leeway; } trx_context.deadline = deadline; + trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time; trx_context.billed_cpu_time_us = billed_cpu_time_us; trace = trx_context.trace; try { @@ -810,7 +830,7 @@ struct controller_impl { in_trx_requiring_checks = old_value; }); in_trx_requiring_checks = true; - push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage ); + push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage, true ); } catch( const boost::interprocess::bad_alloc& e ) { elog( "on block transaction failed due to a bad allocation" ); throw; @@ -849,9 +869,9 @@ struct controller_impl { if( receipt.trx.contains() ) { auto& pt = receipt.trx.get(); auto mtrx = std::make_shared(pt); - trace = push_transaction( mtrx, fc::time_point::maximum(), false, receipt.cpu_usage_us); + trace = push_transaction( mtrx, fc::time_point::maximum(), false, receipt.cpu_usage_us, true ); } else if( receipt.trx.contains() ) { - trace = push_scheduled_transaction( receipt.trx.get(), fc::time_point::maximum(), receipt.cpu_usage_us ); + trace = push_scheduled_transaction( receipt.trx.get(), fc::time_point::maximum(), receipt.cpu_usage_us, true ); } else { EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" ); } @@ -1311,13 +1331,13 @@ void controller::push_confirmation( const header_confirmation& c ) { transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline, uint32_t billed_cpu_time_us ) { validate_db_available_size(); - return my->push_transaction(trx, deadline, false, billed_cpu_time_us); + return my->push_transaction(trx, deadline, false, billed_cpu_time_us, billed_cpu_time_us > 0 ); } transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us ) { validate_db_available_size(); - return my->push_scheduled_transaction( trxid, deadline, billed_cpu_time_us ); + return my->push_scheduled_transaction( trxid, deadline, billed_cpu_time_us, billed_cpu_time_us > 0 ); } uint32_t controller::head_block_num()const { diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 8dce65e9456..e82dc234a58 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -39,6 +39,10 @@ namespace eosio { namespace chain { void add_ram_usage( account_name account, int64_t ram_delta ); + uint32_t update_billed_cpu_time( fc::time_point now ); + + std::tuple max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits = false )const; + private: friend struct controller_impl; @@ -81,6 +85,7 @@ namespace eosio { namespace chain { fc::time_point deadline = fc::time_point::maximum(); fc::microseconds leeway = fc::microseconds(3000); int64_t billed_cpu_time_us = 0; + bool explicit_billed_cpu_time = false; private: bool is_initialized = false; @@ -92,6 +97,7 @@ namespace eosio { namespace chain { uint64_t eager_net_limit = 0; uint64_t& net_usage; /// reference to trace->net_usage + fc::microseconds initial_objective_duration_limit; fc::microseconds objective_duration_limit; fc::time_point _deadline = fc::time_point::maximum(); int64_t deadline_exception_code = block_cpu_usage_exceeded::code_value; diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 251d478bbe8..3ed10c0df70 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -70,7 +70,9 @@ namespace eosio { namespace chain { } } - if( billed_cpu_time_us > 0 ) + initial_objective_duration_limit = objective_duration_limit; + + if( billed_cpu_time_us > 0 ) // could also call on explicit_billed_cpu_time but it would be redundant validate_cpu_usage_to_bill( billed_cpu_time_us, false ); // Fail early if the amount to be billed is too high // Record accounts to be billed for network and CPU usage @@ -85,17 +87,11 @@ namespace eosio { namespace chain { rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot ); // Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed - int64_t account_net_limit = large_number_no_overflow; - int64_t account_cpu_limit = large_number_no_overflow; - for( const auto& a : bill_to_accounts ) { - bool elastic = !(control.is_producing_block() && control.is_resource_greylisted(a)); - auto net_limit = rl.get_account_net_limit(a, elastic); - if( net_limit >= 0 ) - account_net_limit = std::min( account_net_limit, net_limit ); - auto cpu_limit = rl.get_account_cpu_limit(a, elastic); - if( cpu_limit >= 0 ) - account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); - } + int64_t account_net_limit = 0; + int64_t account_cpu_limit = 0; + bool greylisted = false; + std::tie( account_net_limit, account_cpu_limit, greylisted ) = max_bandwidth_billed_accounts_can_pay(); + net_limit_due_to_greylist |= greylisted; eager_net_limit = net_limit; @@ -115,7 +111,7 @@ namespace eosio { namespace chain { billing_timer_duration_limit = _deadline - start; // Check if deadline is limited by caller-set deadline (only change deadline if billed_cpu_time_us is not set) - if( billed_cpu_time_us > 0 || deadline < _deadline ) { + if( explicit_billed_cpu_time || deadline < _deadline ) { _deadline = deadline; deadline_exception_code = deadline_exception::code_value; } else { @@ -203,7 +199,6 @@ namespace eosio { namespace chain { void transaction_context::finalize() { EOS_ASSERT( is_initialized, transaction_exception, "must first initialize" ); - const static int64_t large_number_no_overflow = std::numeric_limits::max()/2; if( is_input ) { auto& am = control.get_mutable_authorization_manager(); @@ -220,19 +215,11 @@ namespace eosio { namespace chain { } // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed - int64_t account_net_limit = large_number_no_overflow; - int64_t account_cpu_limit = large_number_no_overflow; - for( const auto& a : bill_to_accounts ) { - bool elastic = !(control.is_producing_block() && control.is_resource_greylisted(a)); - auto net_limit = rl.get_account_net_limit(a, elastic); - if( net_limit >= 0 ) { - account_net_limit = std::min( account_net_limit, net_limit ); - if (!elastic) net_limit_due_to_greylist = true; - } - auto cpu_limit = rl.get_account_cpu_limit(a, elastic); - if( cpu_limit >= 0 ) - account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); - } + int64_t account_net_limit = 0; + int64_t account_cpu_limit = 0; + bool greylisted = false; + std::tie( account_net_limit, account_cpu_limit, greylisted ) = max_bandwidth_billed_accounts_can_pay(); + net_limit_due_to_greylist |= greylisted; // Possibly lower net_limit to what the billed accounts can pay if( static_cast(account_net_limit) <= net_limit ) { @@ -254,10 +241,7 @@ namespace eosio { namespace chain { auto now = fc::time_point::now(); trace->elapsed = now - start; - if( billed_cpu_time_us == 0 ) { - const auto& cfg = control.get_global_properties().configuration; - billed_cpu_time_us = std::max( (now - pseudo_start).count(), static_cast(cfg.min_transaction_cpu_usage) ); - } + update_billed_cpu_time( now ); validate_cpu_usage_to_bill( billed_cpu_time_us ); @@ -295,7 +279,7 @@ namespace eosio { namespace chain { auto now = fc::time_point::now(); if( BOOST_UNLIKELY( now > _deadline ) ) { // edump((now-start)(now-pseudo_start)); - if( billed_cpu_time_us > 0 || deadline_exception_code == deadline_exception::code_value ) { + if( explicit_billed_cpu_time || deadline_exception_code == deadline_exception::code_value ) { EOS_THROW( deadline_exception, "deadline exceeded", ("now", now)("deadline", _deadline)("start", start) ); } else if( deadline_exception_code == block_cpu_usage_exceeded::code_value ) { EOS_THROW( block_cpu_usage_exceeded, @@ -316,7 +300,7 @@ namespace eosio { namespace chain { } void transaction_context::pause_billing_timer() { - if( billed_cpu_time_us > 0 || pseudo_start == fc::time_point() ) return; // either irrelevant or already paused + if( explicit_billed_cpu_time || pseudo_start == fc::time_point() ) return; // either irrelevant or already paused auto now = fc::time_point::now(); billed_time = now - pseudo_start; @@ -325,7 +309,7 @@ namespace eosio { namespace chain { } void transaction_context::resume_billing_timer() { - if( billed_cpu_time_us > 0 || pseudo_start != fc::time_point() ) return; // either irrelevant or already running + if( explicit_billed_cpu_time || pseudo_start != fc::time_point() ) return; // either irrelevant or already running auto now = fc::time_point::now(); pseudo_start = now - billed_time; @@ -370,6 +354,39 @@ namespace eosio { namespace chain { } } + uint32_t transaction_context::update_billed_cpu_time( fc::time_point now ) { + if( explicit_billed_cpu_time ) return static_cast(billed_cpu_time_us); + + const auto& cfg = control.get_global_properties().configuration; + billed_cpu_time_us = std::max( (now - pseudo_start).count(), static_cast(cfg.min_transaction_cpu_usage) ); + + return static_cast(billed_cpu_time_us); + } + + std::tuple transaction_context::max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits )const { + // Assumes rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot ) was already called prior + + // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed + auto& rl = control.get_mutable_resource_limits_manager(); + const static int64_t large_number_no_overflow = std::numeric_limits::max()/2; + int64_t account_net_limit = large_number_no_overflow; + int64_t account_cpu_limit = large_number_no_overflow; + bool greylisted = false; + for( const auto& a : bill_to_accounts ) { + bool elastic = force_elastic_limits || !(control.is_producing_block() && control.is_resource_greylisted(a)); + auto net_limit = rl.get_account_net_limit(a, elastic); + if( net_limit >= 0 ) { + account_net_limit = std::min( account_net_limit, net_limit ); + if (!elastic) greylisted = true; + } + auto cpu_limit = rl.get_account_cpu_limit(a, elastic); + if( cpu_limit >= 0 ) + account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); + } + + return std::make_tuple(account_net_limit, account_cpu_limit, greylisted); + } + void transaction_context::dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) { apply_context acontext( control, *this, a, recurse_depth ); acontext.context_free = context_free; diff --git a/scripts/eosio_build_darwin.sh b/scripts/eosio_build_darwin.sh index 63c500ecc87..346d259e134 100644 --- a/scripts/eosio_build_darwin.sh +++ b/scripts/eosio_build_darwin.sh @@ -216,7 +216,7 @@ done fi printf "\\tInstalling boost libraries.\\n" - if ! "${BREW}" install boost + if ! "${BREW}" install https://raw.githubusercontent.com/Homebrew/homebrew-core/f946d12e295c8a27519b73cc810d06593270a07f/Formula/boost.rb then printf "\\tUnable to install boost 1.67 libraries at this time. 0\\n" printf "\\tExiting now.\\n\\n"