resource_limits.cpp 21.0 KB
Newer Older
1
#include <eosio/chain/exceptions.hpp>
2
#include <eosio/chain/resource_limits.hpp>
3 4 5
#include <eosio/chain/resource_limits_private.hpp>
#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/transaction.hpp>
B
Bart Wyatt 已提交
6 7
#include <algorithm>

8
namespace eosio { namespace chain { namespace resource_limits {
B
Bart Wyatt 已提交
9

10 11
static_assert( config::rate_limiting_precision > 0, "config::rate_limiting_precision must be positive" );

12
static uint64_t update_elastic_limit(uint64_t current_limit, uint64_t average_usage, const elastic_limit_parameters& params) {
B
Bart Wyatt 已提交
13
   uint64_t result = current_limit;
14
   if (average_usage > params.target ) {
B
Bart Wyatt 已提交
15 16 17 18
      result = result * params.contract_rate;
   } else {
      result = result * params.expand_rate;
   }
19
   return std::min(std::max(result, params.max), params.max * params.max_multiplier);
B
Bart Wyatt 已提交
20 21
}

22 23 24 25 26 27 28 29 30
void elastic_limit_parameters::validate()const {
   // At the very least ensure parameters are not set to values that will cause divide by zero errors later on.
   // Stricter checks for sensible values can be added later.
   FC_ASSERT( periods > 0, "elastic limit parameter 'periods' cannot be zero" );
   FC_ASSERT( contract_rate.denominator > 0, "elastic limit parameter 'contract_rate' is not a well-defined ratio" );
   FC_ASSERT( expand_rate.denominator > 0,   "elastic limit parameter 'expand_rate' is not a well-defined ratio" );
}


B
Bart Wyatt 已提交
31
void resource_limits_state_object::update_virtual_cpu_limit( const resource_limits_config_object& cfg ) {
32
   //idump((average_block_cpu_usage.average()));
B
Bart Wyatt 已提交
33
   virtual_cpu_limit = update_elastic_limit(virtual_cpu_limit, average_block_cpu_usage.average(), cfg.cpu_limit_parameters);
34
   //idump((virtual_cpu_limit));
B
Bart Wyatt 已提交
35 36 37 38 39 40
}

void resource_limits_state_object::update_virtual_net_limit( const resource_limits_config_object& cfg ) {
   virtual_net_limit = update_elastic_limit(virtual_net_limit, average_block_net_usage.average(), cfg.net_limit_parameters);
}

41
void resource_limits_manager::add_indices() {
42 43 44 45 46 47
   _db.add_index<resource_limits_index>();
   _db.add_index<resource_usage_index>();
   _db.add_index<resource_limits_state_index>();
   _db.add_index<resource_limits_config_index>();
}

48
void resource_limits_manager::initialize_database() {
49
   const auto& config = _db.create<resource_limits_config_object>([](resource_limits_config_object& config){
50 51 52
      // see default settings in the declaration
   });

53
   _db.create<resource_limits_state_object>([&config](resource_limits_state_object& state){
54
      // see default settings in the declaration
55 56 57 58

      // start the chain off in a way that it is "congested" aka slow-start
      state.virtual_cpu_limit = config.cpu_limit_parameters.max;
      state.virtual_net_limit = config.net_limit_parameters.max;
59 60 61 62
   });
}

void resource_limits_manager::initialize_account(const account_name& account) {
63 64 65 66 67
   _db.create<resource_limits_object>([&]( resource_limits_object& bl ) {
      bl.owner = account;
   });

   _db.create<resource_usage_object>([&]( resource_usage_object& bu ) {
68 69 70 71
      bu.owner = account;
   });
}

72
void resource_limits_manager::set_block_parameters(const elastic_limit_parameters& cpu_limit_parameters, const elastic_limit_parameters& net_limit_parameters ) {
73 74
   cpu_limit_parameters.validate();
   net_limit_parameters.validate();
75 76 77 78 79 80 81
   const auto& config = _db.get<resource_limits_config_object>();
   _db.modify(config, [&](resource_limits_config_object& c){
      c.cpu_limit_parameters = cpu_limit_parameters;
      c.net_limit_parameters = net_limit_parameters;
   });
}

82
void resource_limits_manager::add_transaction_usage(const flat_set<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t time_slot ) {
83 84 85 86 87 88 89 90 91
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& config = _db.get<resource_limits_config_object>();
   set<std::pair<account_name, permission_name>> authorizing_accounts;

   for( const auto& a : accounts ) {

      const auto& usage = _db.get<resource_usage_object,by_owner>( a );
      const auto& limits = _db.get<resource_limits_object,by_owner>( boost::make_tuple(false, a));
      _db.modify( usage, [&]( auto& bu ){
92 93
          bu.net_usage.add( net_usage, time_slot, config.account_net_usage_average_window );
          bu.cpu_usage.add( cpu_usage, time_slot, config.account_cpu_usage_average_window );
94 95
      });

96 97 98 99
      if (limits.cpu_weight >= 0 && state.total_cpu_weight > 0 ) {
         uint128_t window_size = config.account_cpu_usage_average_window;
         auto virtual_network_capacity_in_window = state.virtual_cpu_limit * window_size;
         auto cpu_used_in_window                 = (usage.cpu_usage.value_ex * window_size) / config::rate_limiting_precision;
100

101 102 103 104 105 106
         uint128_t user_weight     = limits.cpu_weight;
         uint128_t all_user_weight = state.total_cpu_weight;

         auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;

         EOS_ASSERT( cpu_used_in_window <= max_user_use_in_window, 
107
                     tx_cpu_usage_exceeded,
108 109 110 111 112
                     "authorizing account '${n}' has insufficient cpu resources for this transaction", 
                     ("n", name(a))
                     ("cpu_used_in_window",cpu_used_in_window)
                     ("max_user_use_in_window",max_user_use_in_window) );

113 114
      }

115 116 117 118 119 120 121 122
      if( limits.net_weight >= 0 && state.total_net_weight > 0) {

         uint128_t window_size = config.account_net_usage_average_window;
         auto virtual_network_capacity_in_window = state.virtual_net_limit * window_size;
         auto net_used_in_window                 = (usage.net_usage.value_ex * window_size) / config::rate_limiting_precision;

         uint128_t user_weight     = limits.net_weight;
         uint128_t all_user_weight = state.total_net_weight;
123

124 125 126
         auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;

         EOS_ASSERT( net_used_in_window <= max_user_use_in_window, 
127
                     tx_net_usage_exceeded,
128 129 130 131 132
                     "authorizing account '${n}' has insufficient net resources for this transaction", 
                     ("n", name(a))
                     ("net_used_in_window",net_used_in_window)
                     ("max_user_use_in_window",max_user_use_in_window) );

133
      }
134
   }
135 136 137 138 139 140 141 142 143

   // account for this transaction in the block and do not exceed those limits either
   _db.modify(state, [&](resource_limits_state_object& rls){
      rls.pending_cpu_usage += cpu_usage;
      rls.pending_net_usage += net_usage;
   });

   EOS_ASSERT( state.pending_cpu_usage <= config.cpu_limit_parameters.max, block_resource_exhausted, "Block has insufficient cpu resources" );
   EOS_ASSERT( state.pending_net_usage <= config.net_limit_parameters.max, block_resource_exhausted, "Block has insufficient net resources" );
144 145
}

146
void resource_limits_manager::add_pending_ram_usage( const account_name account, int64_t ram_delta ) {
147 148 149 150
   if (ram_delta == 0) {
      return;
   }

151
   const auto& usage  = _db.get<resource_usage_object,by_owner>( account );
B
Bart Wyatt 已提交
152

153 154 155 156 157
   EOS_ASSERT( ram_delta <= 0 || UINT64_MAX - usage.ram_usage >= (uint64_t)ram_delta, transaction_exception,
              "Ram usage delta would overflow UINT64_MAX");
   EOS_ASSERT(ram_delta >= 0 || usage.ram_usage >= (uint64_t)(-ram_delta), transaction_exception,
              "Ram usage delta would underflow UINT64_MAX");

158 159
   _db.modify( usage, [&]( auto& u ) {
     u.ram_usage += ram_delta;
B
Bart Wyatt 已提交
160
   });
161
}
B
Bart Wyatt 已提交
162

163 164 165 166
void resource_limits_manager::verify_account_ram_usage( const account_name account )const {
   int64_t ram_bytes; int64_t net_weight; int64_t cpu_weight;
   get_account_limits( account, ram_bytes, net_weight, cpu_weight );
   const auto& usage  = _db.get<resource_usage_object,by_owner>( account );
167

168 169 170 171
   if( ram_bytes >= 0 ) {
      EOS_ASSERT( usage.ram_usage <= ram_bytes, ram_usage_exceeded,
                  "account ${account} has insufficient ram bytes; needs ${available} has ${needs}",
                  ("account", account)("available",usage.ram_usage)("needs",ram_bytes)              );
172 173 174
   }
}

175 176 177 178
int64_t resource_limits_manager::get_account_ram_usage( const account_name& name )const {
   return _db.get<resource_usage_object,by_owner>( name ).ram_usage;
}

179

180
bool resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) {
181
   const auto& usage = _db.get<resource_usage_object,by_owner>( account );
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
   /*
    * Since we need to delay these until the next resource limiting boundary, these are created in a "pending"
    * state or adjusted in an existing "pending" state.  The chain controller will collapse "pending" state into
    * the actual state at the next appropriate boundary.
    */
   auto find_or_create_pending_limits = [&]() -> const resource_limits_object& {
      const auto* pending_limits = _db.find<resource_limits_object, by_owner>( boost::make_tuple(true, account) );
      if (pending_limits == nullptr) {
         const auto& limits = _db.get<resource_limits_object, by_owner>( boost::make_tuple(false, account));
         return _db.create<resource_limits_object>([&](resource_limits_object& pending_limits){
            pending_limits.owner = limits.owner;
            pending_limits.ram_bytes = limits.ram_bytes;
            pending_limits.net_weight = limits.net_weight;
            pending_limits.cpu_weight = limits.cpu_weight;
            pending_limits.pending = true;
         });
      } else {
         return *pending_limits;
      }
   };

   // update the users weights directly
   auto& limits = find_or_create_pending_limits();
205

206 207 208 209 210 211 212 213
   bool decreased_limit = false;

   if( ram_bytes >= 0 ) {

      decreased_limit = ( (limits.ram_bytes < 0) || (ram_bytes < limits.ram_bytes) );

      /*
      if( limits.ram_bytes < 0 ) {
214 215 216 217
         EOS_ASSERT(ram_bytes >= usage.ram_usage, wasm_execution_error, "converting unlimited account would result in overcommitment [commit=${c}, desired limit=${l}]", ("c", usage.ram_usage)("l", ram_bytes));
      } else {
         EOS_ASSERT(ram_bytes >= usage.ram_usage, wasm_execution_error, "attempting to release committed ram resources [commit=${c}, desired limit=${l}]", ("c", usage.ram_usage)("l", ram_bytes));
      }
218
      */
219 220
   }

221 222 223 224 225 226
   auto old_ram_bytes = limits.ram_bytes;
   _db.modify( limits, [&]( resource_limits_object& pending_limits ){
      pending_limits.ram_bytes = ram_bytes;
      pending_limits.net_weight = net_weight;
      pending_limits.cpu_weight = cpu_weight;
   });
227 228

   return decreased_limit;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
}

void resource_limits_manager::get_account_limits( const account_name& account, int64_t& ram_bytes, int64_t& net_weight, int64_t& cpu_weight ) const {
   const auto* pending_buo = _db.find<resource_limits_object,by_owner>( 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 = _db.get<resource_limits_object,by_owner>( boost::make_tuple( false, account ) );
      ram_bytes  = buo.ram_bytes;
      net_weight = buo.net_weight;
      cpu_weight = buo.cpu_weight;
   }
}

B
Bart Wyatt 已提交
245

246
void resource_limits_manager::process_account_limit_updates() {
247 248 249
   auto& multi_index = _db.get_mutable_index<resource_limits_index>();
   auto& by_owner_index = multi_index.indices().get<by_owner>();

250 251 252 253 254 255
   // 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;
      }
256

257
      if (pending_value > 0) {
258
         EOS_ASSERT(UINT64_MAX - total >= pending_value, rate_limiting_state_inconsistent, "overflow when applying new value to ${which}", ("which", debug_which));
259
         total += pending_value;
260 261
      }

262 263
      value = pending_value;
   };
264

265
   const auto& state = _db.get<resource_limits_state_object>();
266
   _db.modify(state, [&](resource_limits_state_object& rso){
267 268 269 270 271 272 273 274 275 276
      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<resource_limits_object, by_owner>(boost::make_tuple(false, itr->owner));
         _db.modify(actual_entry, [&](resource_limits_object& rlo){
            update_state_and_value(rso.total_ram_bytes,  rlo.ram_bytes,  itr->ram_bytes, "ram_bytes");
            update_state_and_value(rso.total_cpu_weight, rlo.cpu_weight, itr->cpu_weight, "cpu_weight");
B
Brian Johnson 已提交
277
            update_state_and_value(rso.total_net_weight, rlo.net_weight, itr->net_weight, "net_weight");
278
         });
279

280 281
         multi_index.remove(*itr);
      }
282 283
   });
}
B
Bart Wyatt 已提交
284

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
void resource_limits_manager::process_block_usage(uint32_t block_num) {
   const auto& s = _db.get<resource_limits_state_object>();
   const auto& config = _db.get<resource_limits_config_object>();
   _db.modify(s, [&](resource_limits_state_object& state){
      // apply pending usage, update virtual limits and reset the pending

      state.average_block_cpu_usage.add(state.pending_cpu_usage, block_num, config.cpu_limit_parameters.periods);
      state.update_virtual_cpu_limit(config);
      state.pending_cpu_usage = 0;

      state.average_block_net_usage.add(state.pending_net_usage, block_num, config.net_limit_parameters.periods);
      state.update_virtual_net_limit(config);
      state.pending_net_usage = 0;

   });
300

301 302
}

303 304 305 306 307 308 309 310 311 312
uint64_t resource_limits_manager::get_virtual_block_cpu_limit() const {
   const auto& state = _db.get<resource_limits_state_object>();
   return state.virtual_cpu_limit;
}

uint64_t resource_limits_manager::get_virtual_block_net_limit() const {
   const auto& state = _db.get<resource_limits_state_object>();
   return state.virtual_net_limit;
}

313 314 315 316 317 318 319 320 321 322 323 324 325
uint64_t resource_limits_manager::get_block_cpu_limit() const {
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& config = _db.get<resource_limits_config_object>();
   return config.cpu_limit_parameters.max - state.pending_cpu_usage;
}

uint64_t resource_limits_manager::get_block_net_limit() const {
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& config = _db.get<resource_limits_config_object>();
   return config.net_limit_parameters.max - state.pending_net_usage;
}

int64_t resource_limits_manager::get_account_cpu_limit( const account_name& name ) const {
326

327 328
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& usage = _db.get<resource_usage_object, by_owner>(name);
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
   const auto& config = _db.get<resource_limits_config_object>();

   int64_t unused;
   int64_t cpu_weight;
   get_account_limits( name, unused, unused, cpu_weight );

   if( cpu_weight < 0 ) {
      return -1;
   }

   uint128_t window_size = config.account_cpu_usage_average_window;

   auto virtual_cpuwork_capacity_in_window = state.virtual_cpu_limit * window_size;
   uint128_t user_weight     = cpu_weight;
   uint128_t all_user_weight = state.total_cpu_weight;

   auto max_user_use_in_window = (virtual_cpuwork_capacity_in_window * user_weight) / all_user_weight;
   auto cpu_used_in_window  = (usage.cpu_usage.value_ex * window_size) / config::rate_limiting_precision;

   if( max_user_use_in_window <= cpu_used_in_window ) return 0;

   return max_user_use_in_window - cpu_used_in_window;

/*
353 354
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& usage = _db.get<resource_usage_object, by_owner>(name);
A
arhag 已提交
355 356 357 358 359 360

   int64_t x;
   int64_t cpu_weight;
   get_account_limits( name, x, x, cpu_weight );

   if( cpu_weight < 0 ) {
361 362 363
      return -1;
   }

364 365 366
   auto total_cpu_weight = state.total_cpu_weight;
   if( total_cpu_weight == 0 ) total_cpu_weight = 1;

367 368
   uint128_t consumed_ex = (uint128_t)usage.cpu_usage.consumed * (uint128_t)config::rate_limiting_precision;
   uint128_t virtual_capacity_ex = (uint128_t)state.virtual_cpu_limit * (uint128_t)config::rate_limiting_precision;
369

A
arhag 已提交
370
   uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
371

A
arhag 已提交
372
   if( usable_capacity_ex < consumed_ex ) {
373 374 375 376
      return 0;
   }

   return (int64_t)((usable_capacity_ex - consumed_ex) / (uint128_t)config::rate_limiting_precision);
377
   */
378 379
}

380 381 382 383
account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const account_name& name ) const {
   const auto& cfg = _db.get<resource_limits_config_object>();
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& usage = _db.get<resource_usage_object, by_owner>(name);
A
arhag 已提交
384 385 386 387 388 389

   int64_t x;
   int64_t cpu_weight;
   get_account_limits( name, x, x, cpu_weight );

   if( cpu_weight < 0 ) {
390 391 392 393 394 395 396 397 398
      return { -1, -1, -1 };
   }

   auto total_cpu_weight = state.total_cpu_weight;
   if( total_cpu_weight == 0 ) total_cpu_weight = 1;

   uint128_t consumed_ex = (uint128_t)usage.cpu_usage.consumed * (uint128_t)config::rate_limiting_precision;
   uint128_t virtual_capacity_ex = (uint128_t)state.virtual_cpu_limit * (uint128_t)config::rate_limiting_precision;

A
arhag 已提交
399
   uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
400 401

   uint128_t real_capacity_ex = (uint128_t)cfg.cpu_limit_parameters.target * (uint128_t)config::rate_limiting_precision;
A
arhag 已提交
402
   uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
403 404 405

   uint128_t blocks_per_day = 86400 * 1000 / config::block_interval_ms;

A
arhag 已提交
406
   if( usable_capacity_ex < consumed_ex ) {
407 408 409 410 411 412 413 414
      consumed_ex = usable_capacity_ex;
   }
   return { (int64_t)(std::min(usable_capacity_ex - consumed_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
         (int64_t)(std::min(usable_capacity_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
         (int64_t)(guaranteed_capacity_ex * blocks_per_day / cfg.cpu_limit_parameters.periods / (uint128_t)config::rate_limiting_precision)
         };
}

415
int64_t resource_limits_manager::get_account_net_limit( const account_name& name ) const {
416 417
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& usage = _db.get<resource_usage_object, by_owner>(name);
418
   const auto& config = _db.get<resource_limits_config_object>();
A
arhag 已提交
419

420
   int64_t unused;
A
arhag 已提交
421
   int64_t net_weight;
422
   get_account_limits( name, unused, net_weight, unused );
A
arhag 已提交
423 424

   if( net_weight < 0 ) {
425 426 427
      return -1;
   }

428 429 430 431 432 433 434 435 436 437 438 439 440 441
   uint128_t window_size = config.account_net_usage_average_window;

   auto virtual_network_capacity_in_window = state.virtual_net_limit * window_size;
   uint128_t user_weight     = net_weight;
   uint128_t all_user_weight = state.total_net_weight;

   auto max_user_use_in_window = (virtual_network_capacity_in_window * user_weight) / all_user_weight;
   auto net_used_in_window  = (usage.net_usage.value_ex * window_size) / config::rate_limiting_precision;

   if( max_user_use_in_window <= net_used_in_window ) return 0;

   return max_user_use_in_window - net_used_in_window;

   /*
442 443
   uint128_t consumed_ex = (uint128_t)usage.net_usage.consumed * (uint128_t)config::rate_limiting_precision;
   uint128_t virtual_capacity_ex = (uint128_t)state.virtual_net_limit * (uint128_t)config::rate_limiting_precision;
444 445 446 447

   auto total_net_weight = state.total_net_weight;
   if( total_net_weight == 0 ) total_net_weight = 1;

A
arhag 已提交
448
   uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * net_weight) / (uint128_t)total_net_weight; // max
449

A
arhag 已提交
450
   if( usable_capacity_ex < consumed_ex ) {
451 452 453 454
      return 0;
   }

   return (int64_t)((usable_capacity_ex - consumed_ex) / (uint128_t)config::rate_limiting_precision);
455
   */
456 457
}

458 459 460 461
account_resource_limit resource_limits_manager::get_account_net_limit_ex( const account_name& name ) const {
   const auto& cfg = _db.get<resource_limits_config_object>();
   const auto& state = _db.get<resource_limits_state_object>();
   const auto& usage = _db.get<resource_usage_object, by_owner>(name);
A
arhag 已提交
462 463 464 465 466 467

   int64_t x;
   int64_t net_weight;
   get_account_limits( name, x, net_weight, x );

   if( net_weight < 0 ) {
468 469 470 471 472 473 474 475 476
      return { -1, -1, -1 };
   }

   auto total_net_weight = state.total_net_weight;
   if( total_net_weight == 0 ) total_net_weight = 1;

   uint128_t consumed_ex = (uint128_t)usage.net_usage.consumed * (uint128_t)config::rate_limiting_precision;
   uint128_t virtual_capacity_ex = (uint128_t)state.virtual_net_limit * (uint128_t)config::rate_limiting_precision;

A
arhag 已提交
477
   uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * net_weight) / (uint128_t)total_net_weight; // max
478 479

   uint128_t real_capacity_ex = (uint128_t)cfg.net_limit_parameters.target * (uint128_t)config::rate_limiting_precision;
A
arhag 已提交
480
   uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * net_weight) / (uint128_t)total_net_weight;
481 482 483

   uint128_t blocks_per_day = 86400 * 1000 / config::block_interval_ms;

A
arhag 已提交
484
   if( usable_capacity_ex < consumed_ex ) {
485 486 487 488 489 490 491 492
      consumed_ex = usable_capacity_ex;
   }
   return { (int64_t)(std::min(usable_capacity_ex - consumed_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
         (int64_t)(std::min(usable_capacity_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
         (int64_t)(guaranteed_capacity_ex * blocks_per_day / cfg.net_limit_parameters.periods / (uint128_t)config::rate_limiting_precision)
         };
}

493

494
} } } /// eosio::chain::resource_limits