diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index d7435023372..9ec1efdb692 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1010,36 +1010,38 @@ struct controller_impl { return push_scheduled_transaction( *itr, deadline, billed_cpu_time_us, explicit_billed_cpu_time ); } - - void call_approvebw_action(const action& action, fc::time_point deadline) { - const auto request_bw = action.data_as(); - + std::map call_approvebw_actions(vector&& actions, fc::time_point deadline) { signed_transaction call_provide_trx; - call_provide_trx.actions.emplace_back(vector{{request_bw.provider, config::active_name}}, request_bw.provider, N(approvebw), fc::raw::pack(approvebw( request_bw.account))); + call_provide_trx.actions = std::move(actions); call_provide_trx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to avoid appearing expired call_provide_trx.set_reference_block( self.head_block_id() ); transaction_context trx_context( self, call_provide_trx, call_provide_trx.id()); trx_context.deadline = deadline; - transaction_trace_ptr trace = trx_context.trace; trx_context.init_for_implicit_trx(); - trx_context.trace->action_traces.emplace_back(); - trx_context.dispatch_action( trx_context.trace->action_traces.back(), call_provide_trx.actions.back(), request_bw.provider ); + trx_context.exec(); trx_context.finalize(); auto restore = make_block_restore_point(); fc::move_append( pending->_actions, move(trx_context.executed) ); trx_context.squash(); restore.cancel(); + return trx_context.get_provided_bandwith(); } - void verify_approvebw_required(const vector& actions, fc::time_point deadline) { - for (const auto& action : actions) { + std::map verify_bandwith_provided(const vector& actions, fc::time_point deadline) { + std::vector approve_actions; + for (const auto& action : actions) { if (action.account == N(eosio) && action.name == N(requestbw)) { - call_approvebw_action(action, deadline); + const auto request_bw = action.data_as(); + approve_actions.emplace_back(vector{{request_bw.provider, config::active_name}}, request_bw.provider, N(approvebw), fc::raw::pack(approvebw( request_bw.account))); } } + if (!approve_actions.empty()) { + return call_approvebw_actions(std::move(approve_actions), deadline); + } + return {}; } 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 ) @@ -1098,7 +1100,7 @@ struct controller_impl { trx_context.billed_cpu_time_us = billed_cpu_time_us; trace = trx_context.trace; try { - verify_approvebw_required(trx->trx.actions, deadline); + trx_context.set_provided_bandwith(verify_bandwith_provided(trx->trx.actions, deadline)); trx_context.init_for_deferred_trx( gtrx.published ); trx_context.exec(); @@ -1228,7 +1230,7 @@ struct controller_impl { trx_context.billed_cpu_time_us = billed_cpu_time_us; trace = trx_context.trace; try { - verify_approvebw_required(trx->trx.actions, deadline); + trx_context.set_provided_bandwith(verify_bandwith_provided(trx->trx.actions, deadline)); if( trx->implicit ) { trx_context.init_for_implicit_trx(); @@ -1245,7 +1247,6 @@ struct controller_impl { check_actor_list( trx_context.bill_to_accounts ); // Assumes bill_to_accounts is the set of actors authorizing the transaction } - trx_context.delay = fc::seconds(trx->trx.delay_sec); if( !self.skip_auth_check() && !trx->implicit ) { diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index ceae678c7cb..6ea13868b72 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -196,6 +196,8 @@ namespace eosio { namespace chain { 3040013, "Transaction is too big" ) FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_compression, transaction_exception, 3040014, "Unknown transaction compression" ) + FC_DECLARE_DERIVED_EXCEPTION( bandwith_confirmed, transaction_exception, + 3040015, "Bandwith has been already confirmed" ) FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception, @@ -257,7 +259,6 @@ namespace eosio { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( binaryen_exception, wasm_exception, 3070005, "binaryen exception" ) - FC_DECLARE_DERIVED_EXCEPTION( resource_exhausted_exception, chain_exception, 3080000, "Resource exhausted exception" ) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index c87d0296c2e..97a46716990 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -6,12 +6,31 @@ namespace eosio { namespace chain { struct provided_bandwith { - provided_bandwith(uint64_t net_limit, uint64_t cpu_limit) - : net_limit(net_limit), - cpu_limit(cpu_limit) {} + provided_bandwith() = default; - uint64_t net_limit; - uint64_t cpu_limit; +// provided_bandwith(account_name account) +// : account_(account) {} + + void confirm(account_name provider); + + bool is_confirmed() const {return confirmed_;} + + int64_t get_net_limit() const {return net_limit_;} + int64_t get_cpu_limit() const {return cpu_limit_;} + + void set_net_limit(int64_t net_limit); + void set_cpu_limit(int64_t cpu_limit); + + account_name get_provider() const {return provider_;} + + private: + + void verify_limits_not_confirmed(); + + int64_t net_limit_ = 0; + int64_t cpu_limit_ = 0; + bool confirmed_ = false; + account_name provider_; }; struct deadline_timer { @@ -65,9 +84,19 @@ namespace eosio { namespace chain { std::tuple max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits = false )const; - uint64_t get_provided_net_limit() const {return provided_bandwith_.net_limit;} + uint64_t get_provided_net_limit(account_name account) const; + + uint64_t get_provided_cpu_limit(account_name account) const; + + std::map get_provided_bandwith() const {return provided_bandwith_;} + + bool is_provided_bandwith_confirmed(account_name account) const; + + void set_provided_bandwith(std::map&& bandwith); + + void set_provided_bandwith_limits(account_name account, uint64_t net_limit, uint64_t cpu_limit); - uint64_t get_provided_cpu_limit() const {return provided_bandwith_.cpu_limit;} + void confirm_provided_bandwith_limits(account_name account, account_name provider); private: @@ -85,7 +114,6 @@ namespace eosio { namespace chain { void validate_cpu_usage_to_bill( int64_t u, bool check_minimum = true )const; - provided_bandwith get_provided_bandwith() const; /// Fields: public: @@ -141,7 +169,7 @@ namespace eosio { namespace chain { deadline_timer _deadline_timer; - provided_bandwith provided_bandwith_; + std::map provided_bandwith_; }; } } diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index b76286bd743..b15665eb187 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -85,6 +85,28 @@ namespace bacc = boost::accumulators; volatile sig_atomic_t deadline_timer_verify::hit; static deadline_timer_verify deadline_timer_verification; + void provided_bandwith::confirm(account_name provider) { + verify_limits_not_confirmed(); + confirmed_ = true; + provider_ = provider; + } + + void provided_bandwith::set_net_limit(int64_t net_limit) { + verify_limits_not_confirmed(); + this->net_limit_ = net_limit; + } + + void provided_bandwith::set_cpu_limit(int64_t cpu_limit) { + verify_limits_not_confirmed(); + this->cpu_limit_ = cpu_limit; + } + + void provided_bandwith::verify_limits_not_confirmed() { + if (confirmed_) { + EOS_THROW( bandwith_confirmed, "bandwith has been already confirmed. No changes could be done"); + } + } + deadline_timer::deadline_timer() { if(initialized) return; @@ -161,7 +183,6 @@ namespace bacc = boost::accumulators; ,start(s) ,net_usage(trace->net_usage) ,pseudo_start(s) - ,provided_bandwith_(get_provided_bandwith()) { if (!c.skip_db_sessions()) { // TODO: removed by CyberWay @@ -185,8 +206,8 @@ namespace bacc = boost::accumulators; auto& rl = control.get_mutable_resource_limits_manager(); net_limit = rl.get_block_net_limit(); - objective_duration_limit = fc::microseconds( rl.get_block_cpu_limit() ); + _deadline = start + objective_duration_limit; // Possibly lower net_limit to the maximum net usage a transaction is allowed to be billed @@ -232,7 +253,12 @@ namespace bacc = boost::accumulators; provided_accounts.insert(args.account); } for( const auto& auth : act.authorization ) { - bill_to_accounts.insert( auth.actor ); + const auto provided_bw_it = provided_bandwith_.find(auth.actor); + if(provided_bw_it != provided_bandwith_.end()) { + bill_to_accounts.insert( provided_bw_it->second.get_provider() ); + } else { + bill_to_accounts.insert( auth.actor ); + } } } validate_ram_usage.reserve( bill_to_accounts.size() ); @@ -560,11 +586,6 @@ namespace bacc = boost::accumulators; return static_cast(billed_cpu_time_us); } - provided_bandwith transaction_context::get_provided_bandwith() const { - const auto max_bandwith = max_bandwidth_billed_accounts_can_pay(); - return {static_cast(std::get<0>(max_bandwith)), static_cast(std::get<1>(max_bandwith))}; - } - 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 @@ -577,12 +598,14 @@ namespace bacc = boost::accumulators; bool greylisted_cpu = 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); + const auto provided_bw_it = provided_bandwith_.find(a); + const auto is_bw_provided = provided_bw_it != provided_bandwith_.end() && provided_bw_it->second.is_confirmed(); + auto net_limit = is_bw_provided ? provided_bw_it->second.get_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_net = true; } - auto cpu_limit = rl.get_account_cpu_limit(a, elastic); + auto cpu_limit = is_bw_provided ? provided_bw_it->second.get_cpu_limit() : rl.get_account_cpu_limit(a, elastic); if( cpu_limit >= 0 ) { account_cpu_limit = std::min( account_cpu_limit, cpu_limit ); if (!elastic) greylisted_cpu = true; @@ -592,6 +615,49 @@ namespace bacc = boost::accumulators; return std::make_tuple(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu); } + uint64_t transaction_context::get_provided_net_limit(account_name account) const { + const auto provided_bw_it = provided_bandwith_.find(account); + + if (provided_bw_it == provided_bandwith_.end()) { + return 0; + } + + return provided_bw_it->second.get_net_limit(); + } + + uint64_t transaction_context::get_provided_cpu_limit(account_name account) const { + const auto provided_bw_it = provided_bandwith_.find(account); + + if (provided_bw_it == provided_bandwith_.end()) { + return 0; + } + + return provided_bw_it->second.get_cpu_limit(); + } + + bool transaction_context::is_provided_bandwith_confirmed(account_name account) const { + const auto provided_bw_it = provided_bandwith_.find(account); + + if (provided_bw_it == provided_bandwith_.end()) { + return 0; + } + + return provided_bw_it->second.is_confirmed(); + } + + void transaction_context::set_provided_bandwith(std::map&& bandwith) { + provided_bandwith_ = std::move(bandwith); + } + + void transaction_context::set_provided_bandwith_limits(account_name account, uint64_t net_limit, uint64_t cpu_limit) { + provided_bandwith_[account].set_net_limit(net_limit); + provided_bandwith_[account].set_cpu_limit(cpu_limit); + } + + void transaction_context::confirm_provided_bandwith_limits(account_name account, account_name provider) { + provided_bandwith_[account].confirm(provider); + } + 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; @@ -634,7 +700,7 @@ namespace bacc = boost::accumulators; transaction.trx_id = id; transaction.expiration = expire; }); - } /// record_transaction - + } + /// record_transaction } } /// eosio::chain diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index ccff46681f3..89a12e0c9b9 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1134,12 +1134,24 @@ class bandwith_api : public context_aware_api { bandwith_api( apply_context& ctx ) : context_aware_api(ctx,true) {} - int64_t get_bw_cpu_limit() const { - return context.trx_context.get_provided_cpu_limit(); + int64_t get_bw_cpu_limit(account_name account) const { + return context.trx_context.get_provided_cpu_limit(account); } - int64_t get_bw_net_limit() const { - return context.trx_context.get_provided_net_limit(); + int64_t get_bw_net_limit(account_name account) const { + return context.trx_context.get_provided_net_limit(account); + } + + bool is_provided_bw_confirmed(account_name account) const { + return context.trx_context.is_provided_bandwith_confirmed(account); + } + + void set_bw_limits(account_name account, int64_t net_limit, int64_t cpu_limit) { + context.trx_context.set_provided_bandwith_limits(account, net_limit, cpu_limit); + } + + void confirm_bw_limits(account_name account) { + context.trx_context.confirm_provided_bandwith_limits(account, context.receiver); } }; @@ -1873,8 +1885,11 @@ REGISTER_INTRINSICS(console_api, REGISTER_INTRINSICS(bandwith_api, - (get_bw_cpu_limit, int64_t()) - (get_bw_net_limit, int64_t()) + (get_bw_cpu_limit, int64_t(int64_t)) + (get_bw_net_limit, int64_t(int64_t )) + (is_provided_bw_confirmed, int(int64_t)) + (set_bw_limits, void(int64_t, int64_t, int64_t)) + (confirm_bw_limits, void(int64_t)) );