diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 6859dbe3b0258514dae3d08fef517037c8af7ab8..806b60231dd84fd3d23c95492dd238fc2be8cfaf 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -1341,15 +1342,45 @@ void chain_controller::update_last_irreversible_block() void chain_controller::update_rate_limiting() { - const auto* ptu = _db.find(); - if (ptu) { - auto& gdp = get_dynamic_global_properties(); - _db.modify( gdp, [&]( dynamic_global_property_object& p ) { - p.total_net_weight = ptu->total_net_weight; - p.total_cpu_weight = ptu->total_cpu_weight; - p.total_db_capacity = ptu->total_db_capacity; + auto& resource_limits_index = _db.get_mutable_index(); + auto& by_owner_index = resource_limits_index.indices().get(); + + auto updated_state = _db.get(); + + while(!by_owner_index.empty()) { + const auto& itr = by_owner_index.lower_bound(boost::make_tuple(true)); + if (itr == by_owner_index.end() || itr->pending!= true) { + break; + } + + const auto& actual_entry = _db.get(boost::make_tuple(false, itr->owner)); + _db.modify(actual_entry, [&](resource_limits_object& rlo){ + // convenience local lambda to reduce clutter + auto update_state_and_value = [](uint64_t &total, int64_t &value, int64_t pending_value, const char* debug_which) -> void { + if (value > 0) { + EOS_ASSERT(total >= value, rate_limiting_state_inconsistent, "underflow when reverting old value to ${which}", ("which", debug_which)); + total -= value; + } + + if (pending_value > 0) { + EOS_ASSERT(UINT64_MAX - total < value, rate_limiting_state_inconsistent, "overflow when applying new value to ${which}", ("which", debug_which)); + total += pending_value; + } + + value = pending_value; + }; + + update_state_and_value(updated_state.total_ram_bytes, rlo.ram_bytes, itr->ram_bytes, "ram_bytes"); + update_state_and_value(updated_state.total_cpu_weight, rlo.cpu_weight, itr->cpu_weight, "cpu_weight"); + update_state_and_value(updated_state.total_net_weight, rlo.net_weight, itr->net_weight, "net_wright"); }); + + by_owner_index.remove(*itr); } + + _db.modify(updated_state, [&updated_state](resource_limits_state_object rso){ + rso = updated_state; + }); } void chain_controller::clear_expired_transactions() diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index a3a252e51737b84ad789f78ae4001110b4e2d5f6..a92f496a2f9ca458b648debbae52fbae7877fb3e 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -22,6 +22,7 @@ namespace eosio { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception, eosio::chain::chain_exception, 3110000, "unknown block" ) FC_DECLARE_DERIVED_EXCEPTION( chain_type_exception, eosio::chain::chain_exception, 3120000, "chain type exception" ) FC_DECLARE_DERIVED_EXCEPTION( missing_plugin_exception, eosio::chain::chain_exception, 3130000, "missing plugin exception" ) + FC_DECLARE_DERIVED_EXCEPTION( rate_limiting_invariant_exception, eosio::chain::chain_exception, 3140000, "rate limiting invariant violated" ) FC_DECLARE_DERIVED_EXCEPTION( permission_query_exception, eosio::chain::database_query_exception, 3010001, "permission query exception" ) @@ -68,6 +69,8 @@ namespace eosio { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( missing_account_history_api_plugin_exception, eosio::chain::missing_plugin_exception, 3130003, "Missing Account History API Plugin" ) FC_DECLARE_DERIVED_EXCEPTION( missing_net_api_plugin_exception, eosio::chain::missing_plugin_exception, 3130003, "Missing Net API Plugin" ) + FC_DECLARE_DERIVED_EXCEPTION( rate_limiting_state_inconsistent, eosio::chain::rate_limiting_invariant_exception, 3140001, "internal state is no longer consistent" ) + #define EOS_RECODE_EXC( cause_type, effect_type ) \ catch( const cause_type& e ) \ diff --git a/libraries/chain/include/eosio/chain/rate_limiting.hpp b/libraries/chain/include/eosio/chain/rate_limiting.hpp index e43d609536d7c9d02745a93dad91c239bb4b2789..e7757868d6694440d6b3ff7eb21bd4d2b13a7fc9 100644 --- a/libraries/chain/include/eosio/chain/rate_limiting.hpp +++ b/libraries/chain/include/eosio/chain/rate_limiting.hpp @@ -68,8 +68,8 @@ namespace eosio { namespace chain { ordered_unique, member>, ordered_unique, composite_key > > diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 5f3f0047ac673169db2eb3cfb509838da03d9179..34e87963e45d3812044b3081215cd3401a78c9bc 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -428,9 +428,9 @@ class privileged_api : public context_aware_api { * the actual state at the next appropriate boundary. */ auto find_or_create_pending_limits = [&]() -> const resource_limits_object& { - const auto* pending_limits = context.db.find( boost::make_tuple(account, true) ); + const auto* pending_limits = context.db.find( boost::make_tuple(true, account) ); if (pending_limits == nullptr) { - const auto& limits = context.db.get( boost::make_tuple(account, false)); + const auto& limits = context.db.get( boost::make_tuple(false, account)); return context.mutable_db.create([&](resource_limits_object& pending_limits){ pending_limits.owner = limits.owner; pending_limits.ram_bytes = limits.ram_bytes; @@ -458,13 +458,13 @@ class privileged_api : public context_aware_api { void get_resource_limits( account_name account, int64_t& ram_bytes, int64_t& net_weight, int64_t cpu_weight ) { - const auto* pending_buo = context.db.find( boost::make_tuple(account, true) ); + const auto* pending_buo = context.db.find( boost::make_tuple(true, account) ); if (pending_buo) { ram_bytes = pending_buo->ram_bytes; net_weight = pending_buo->net_weight; cpu_weight = pending_buo->cpu_weight; } else { - const auto& buo = context.db.get( boost::make_tuple( account, false ) ); + const auto& buo = context.db.get( boost::make_tuple( false, account ) ); ram_bytes = buo.ram_bytes; net_weight = buo.net_weight; cpu_weight = buo.cpu_weight;