7#include <boost/tuple/tuple_io.hpp>
11namespace sysio {
namespace chain {
namespace resource_limits {
20static_assert( config::rate_limiting_precision > 0,
"config::rate_limiting_precision must be positive" );
24 if (average_usage >
params.target ) {
25 result = result *
params.contract_rate;
27 result = result *
params.expand_rate;
35 SYS_ASSERT(
periods > 0, resource_limit_exception,
"elastic limit parameter 'periods' cannot be zero" );
43 virtual_cpu_limit = update_elastic_limit(virtual_cpu_limit, average_block_cpu_usage.
average(), cfg.cpu_limit_parameters);
47void resource_limits_state_object::update_virtual_net_limit(
const resource_limits_config_object& cfg ) {
48 virtual_net_limit = update_elastic_limit(virtual_net_limit, average_block_net_usage.
average(), cfg.net_limit_parameters);
52 resource_index_set::add_indices(_db);
64 state.virtual_cpu_limit =
config.cpu_limit_parameters.max;
65 state.virtual_net_limit =
config.net_limit_parameters.max;
68 if (
auto dm_logger = _get_deep_mind_logger()) {
74 resource_index_set::walk_indices([
this, &snapshot](
auto utils ){
75 snapshot->write_section<
typename decltype(utils)::index_t::value_type>([
this](
auto& section ){
76 decltype(utils)::walk(_db, [
this, §ion](
const auto &row ) {
77 section.add_row(row, _db);
84 resource_index_set::walk_indices([
this, &snapshot](
auto utils ){
85 snapshot->read_section<
typename decltype(utils)::index_t::value_type>([
this](
auto& section ) {
86 bool more = !section.empty();
88 decltype(utils)::create(_db, [
this, §ion, &more](
auto &row ) {
89 more = section.read_row(row, _db);
104 if (
auto dm_logger = _get_deep_mind_logger()) {
105 dm_logger->on_newaccount_resource_limits(limits, usage);
113 if(
config.cpu_limit_parameters == cpu_limit_parameters &&
config.net_limit_parameters == net_limit_parameters )
116 c.cpu_limit_parameters = cpu_limit_parameters;
117 c.net_limit_parameters = net_limit_parameters;
119 if (
auto dm_logger = _get_deep_mind_logger()) {
120 dm_logger->on_update_resource_limits_config(c);
127 for(
const auto&
a : accounts ) {
129 _db.
modify( usage, [&](
auto& bu ){
130 bu.net_usage.add( 0, time_slot,
config.account_net_usage_average_window );
131 bu.cpu_usage.add( 0, time_slot,
config.account_cpu_usage_average_window );
140 for(
const auto&
a : accounts ) {
148 _db.
modify( usage, [&](
auto& bu ){
149 bu.net_usage.add( net_usage, time_slot,
config.account_net_usage_average_window );
150 bu.cpu_usage.add( cpu_usage, time_slot,
config.account_cpu_usage_average_window );
152 if (
auto dm_logger = _get_deep_mind_logger()) {
153 dm_logger->on_update_account_usage(bu);
157 if( cpu_weight >= 0 &&
state.total_cpu_weight > 0 ) {
159 auto virtual_network_capacity_in_window = (
uint128_t)
state.virtual_cpu_limit * window_size;
160 auto cpu_used_in_window = ((
uint128_t)usage.cpu_usage.value_ex * window_size) / (
uint128_t)config::rate_limiting_precision;
165 auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;
167 SYS_ASSERT( cpu_used_in_window <= max_user_use_in_window,
168 tx_cpu_usage_exceeded,
169 "authorizing account '${n}' has insufficient cpu resources for this transaction",
171 (
"cpu_used_in_window",cpu_used_in_window)
172 (
"max_user_use_in_window",max_user_use_in_window) );
175 if( net_weight >= 0 &&
state.total_net_weight > 0) {
178 auto virtual_network_capacity_in_window = (
uint128_t)
state.virtual_net_limit * window_size;
179 auto net_used_in_window = ((
uint128_t)usage.net_usage.value_ex * window_size) / (
uint128_t)config::rate_limiting_precision;
184 auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;
186 SYS_ASSERT( net_used_in_window <= max_user_use_in_window,
187 tx_net_usage_exceeded,
188 "authorizing account '${n}' has insufficient net resources for this transaction",
190 (
"net_used_in_window",net_used_in_window)
191 (
"max_user_use_in_window",max_user_use_in_window) );
198 rls.pending_cpu_usage += cpu_usage;
199 rls.pending_net_usage += net_usage;
202 SYS_ASSERT(
state.pending_cpu_usage <=
config.cpu_limit_parameters.max, block_resource_exhausted,
"Block has insufficient cpu resources" );
203 SYS_ASSERT(
state.pending_net_usage <=
config.net_limit_parameters.max, block_resource_exhausted,
"Block has insufficient net resources" );
207 if (ram_delta == 0) {
214 "Ram usage delta would overflow UINT64_MAX");
216 "Ram usage delta would underflow UINT64_MAX");
218 _db.
modify( usage, [&](
auto& u ) {
219 u.ram_usage += ram_delta;
221 if (
auto dm_logger = _get_deep_mind_logger()) {
222 dm_logger->on_ram_event(account, u.ram_usage, ram_delta);
232 if( ram_bytes >= 0 ) {
234 "account ${account} has insufficient ram; needs ${needs} bytes has ${available} bytes",
235 (
"account", account)(
"needs",usage.ram_usage)(
"available",ram_bytes) );
253 if (pending_limits ==
nullptr) {
256 pending_limits.owner = limits.owner;
257 pending_limits.ram_bytes = limits.ram_bytes;
258 pending_limits.net_weight = limits.net_weight;
259 pending_limits.cpu_weight = limits.cpu_weight;
260 pending_limits.pending =
true;
263 return *pending_limits;
268 auto& limits = find_or_create_pending_limits();
270 bool decreased_limit =
false;
272 if( ram_bytes >= 0 ) {
274 decreased_limit = ( (limits.ram_bytes < 0) || (ram_bytes < limits.ram_bytes) );
290 if (
auto dm_logger = _get_deep_mind_logger()) {
291 dm_logger->on_set_account_limits(pending_limits);
295 return decreased_limit;
301 ram_bytes = pending_buo->ram_bytes;
302 net_weight = pending_buo->net_weight;
303 cpu_weight = pending_buo->cpu_weight;
307 net_weight = buo.net_weight;
308 cpu_weight = buo.cpu_weight;
315 return buo->cpu_weight == -1;
322 auto& by_owner_index = multi_index.indices().get<by_owner>();
327 SYS_ASSERT(total >=
static_cast<uint64_t>(
value), rate_limiting_state_inconsistent,
"underflow when reverting old value to ${which}", (
"which", debug_which));
331 if (pending_value > 0) {
332 SYS_ASSERT(
UINT64_MAX - total >=
static_cast<uint64_t>(pending_value), rate_limiting_state_inconsistent,
"overflow when applying new value to ${which}", (
"which", debug_which));
333 total += pending_value;
336 value = pending_value;
341 while(!by_owner_index.empty()) {
342 const auto& itr = by_owner_index.lower_bound(boost::make_tuple(
true));
343 if (itr == by_owner_index.end() || itr->pending!=
true) {
349 update_state_and_value(rso.total_ram_bytes, rlo.
ram_bytes, itr->ram_bytes,
"ram_bytes");
350 update_state_and_value(rso.total_cpu_weight, rlo.
cpu_weight, itr->cpu_weight,
"cpu_weight");
351 update_state_and_value(rso.total_net_weight, rlo.
net_weight, itr->net_weight,
"net_weight");
354 multi_index.remove(*itr);
357 if (
auto dm_logger = _get_deep_mind_logger()) {
358 dm_logger->on_update_resource_limits_state(
state);
369 state.average_block_cpu_usage.add(
state.pending_cpu_usage, block_num,
config.cpu_limit_parameters.periods);
371 state.pending_cpu_usage = 0;
373 state.average_block_net_usage.add(
state.pending_net_usage, block_num,
config.net_limit_parameters.periods);
375 state.pending_net_usage = 0;
377 if (
auto dm_logger = _get_deep_mind_logger()) {
378 dm_logger->on_update_resource_limits_state(
state);
386 return state.total_cpu_weight;
391 return state.total_net_weight;
396 return state.virtual_cpu_limit;
401 return state.virtual_net_limit;
407 return config.cpu_limit_parameters.max -
state.pending_cpu_usage;
413 return config.net_limit_parameters.max -
state.pending_net_usage;
418 return {arl.available, greylisted};
430 if( cpu_weight < 0 ||
state.total_cpu_weight == 0 ) {
431 return {{ -1, -1, -1 },
false};
438 bool greylisted =
false;
439 uint128_t virtual_cpu_capacity_in_window = window_size;
440 if( greylist_limit < config::maximum_elastic_resource_multiplier ) {
441 uint64_t greylisted_virtual_cpu_limit =
config.cpu_limit_parameters.max * greylist_limit;
442 if( greylisted_virtual_cpu_limit <
state.virtual_cpu_limit ) {
443 virtual_cpu_capacity_in_window *= greylisted_virtual_cpu_limit;
446 virtual_cpu_capacity_in_window *=
state.virtual_cpu_limit;
449 virtual_cpu_capacity_in_window *=
state.virtual_cpu_limit;
455 auto max_user_use_in_window = (virtual_cpu_capacity_in_window * user_weight) / all_user_weight;
458 if( max_user_use_in_window <= cpu_used_in_window )
465 return {arl, greylisted};
470 return {arl.available, greylisted};
481 if( net_weight < 0 ||
state.total_net_weight == 0) {
482 return {{ -1, -1, -1 },
false};
489 bool greylisted =
false;
490 uint128_t virtual_network_capacity_in_window = window_size;
491 if( greylist_limit < config::maximum_elastic_resource_multiplier ) {
492 uint64_t greylisted_virtual_net_limit =
config.net_limit_parameters.max * greylist_limit;
493 if( greylisted_virtual_net_limit <
state.virtual_net_limit ) {
494 virtual_network_capacity_in_window *= greylisted_virtual_net_limit;
497 virtual_network_capacity_in_window *=
state.virtual_net_limit;
500 virtual_network_capacity_in_window *=
state.virtual_net_limit;
506 auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;
509 if( max_user_use_in_window <= net_used_in_window )
516 return {arl, greylisted};
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
void modify(const ObjectType &obj, Modifier &&m)
const ObjectType * find(CompatibleKey &&key) const
const ObjectType & create(Constructor &&con)
generic_index< MultiIndexType > & get_mutable_index()
const ObjectType & get(CompatibleKey &&key) const
bool is_unlimited_cpu(const account_name &account) const
uint64_t get_virtual_block_cpu_limit() const
void get_account_limits(const account_name &account, int64_t &ram_bytes, int64_t &net_weight, int64_t &cpu_weight) const
void initialize_database()
uint64_t get_total_cpu_weight() const
uint64_t get_block_net_limit() const
std::pair< account_resource_limit, bool > get_account_cpu_limit_ex(const account_name &name, uint32_t greylist_limit=config::maximum_elastic_resource_multiplier) const
void initialize_account(const account_name &account)
void read_from_snapshot(const snapshot_reader_ptr &snapshot)
bool set_account_limits(const account_name &account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight)
set_account_limits returns true if new ram_bytes limit is more restrictive than the previously set on...
void set_block_parameters(const elastic_limit_parameters &cpu_limit_parameters, const elastic_limit_parameters &net_limit_parameters)
std::pair< int64_t, bool > get_account_cpu_limit(const account_name &name, uint32_t greylist_limit=config::maximum_elastic_resource_multiplier) const
uint64_t get_block_cpu_limit() const
void process_block_usage(uint32_t block_num)
std::pair< int64_t, bool > get_account_net_limit(const account_name &name, uint32_t greylist_limit=config::maximum_elastic_resource_multiplier) const
void add_to_snapshot(const snapshot_writer_ptr &snapshot) const
void add_pending_ram_usage(const account_name account, int64_t ram_delta)
void process_account_limit_updates()
void update_account_usage(const flat_set< account_name > &accounts, uint32_t ordinal)
void add_transaction_usage(const flat_set< account_name > &accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t ordinal)
int64_t get_account_ram_usage(const account_name &name) const
uint64_t get_virtual_block_net_limit() const
void verify_account_ram_usage(const account_name accunt) const
uint64_t get_total_net_weight() const
std::pair< account_resource_limit, bool > get_account_net_limit_ex(const account_name &name, uint32_t greylist_limit=config::maximum_elastic_resource_multiplier) const
constexpr UnsignedIntType integer_divide_ceil(UnsignedIntType num, UnsignedIntType den)
constexpr auto downgrade_cast(GreaterIntType val) -> std::enable_if_t< is_valid_downgrade_cast< LesserIntType, GreaterIntType > &&std::is_signed< LesserIntType >::value==std::is_signed< GreaterIntType >::value, LesserIntType >
chainbase::shared_multi_index_container< resource_usage_object, indexed_by< ordered_unique< tag< by_id >, member< resource_usage_object, resource_usage_object::id_type, &resource_usage_object::id > >, ordered_unique< tag< by_owner >, member< resource_usage_object, account_name, &resource_usage_object::owner > > > > resource_usage_index
chainbase::shared_multi_index_container< resource_limits_object, indexed_by< ordered_unique< tag< by_id >, member< resource_limits_object, resource_limits_object::id_type, &resource_limits_object::id > >, ordered_unique< tag< by_owner >, composite_key< resource_limits_object, BOOST_MULTI_INDEX_MEMBER(resource_limits_object, bool, pending), > > > > resource_limits_index
chainbase::shared_multi_index_container< resource_limits_state_object, indexed_by< ordered_unique< tag< by_id >, member< resource_limits_state_object, resource_limits_state_object::id_type, &resource_limits_state_object::id > > > > resource_limits_state_index
chainbase::shared_multi_index_container< resource_limits_config_object, indexed_by< ordered_unique< tag< by_id >, member< resource_limits_config_object, resource_limits_config_object::id_type, &resource_limits_config_object::id > > > > resource_limits_config_index
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension transaction_exception
std::shared_ptr< snapshot_writer > snapshot_writer_ptr
unsigned __int128 uint128_t
std::shared_ptr< snapshot_reader > snapshot_reader_ptr
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
unsigned __int64 uint64_t
Immutable except for fc::from_variant.
int64_t used
quantity used in current window
int64_t max
max per window under current congestion
int64_t available
quantity available in current window (based upon fractional reserve)