Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

resource manager audit #3951

Merged
merged 5 commits into from
Jun 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/resource_limits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace eosio { namespace chain { namespace resource_limits {
namespace impl {
template<typename T>
struct ratio {
static_assert(std::is_integral<T>::value, "ratios must have integral types");
T numerator;
T denominator;
};
Expand Down
39 changes: 37 additions & 2 deletions libraries/chain/include/eosio/chain/resource_limits_private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,48 @@ namespace eosio { namespace chain { namespace resource_limits {

template<typename T>
T operator* (T value, const ratio<T>& r) {
EOS_ASSERT(r.numerator == T(0) || std::numeric_limits<T>::max() / r.numerator >= value, rate_limiting_state_inconsistent, "Usage exceeds maximum value representable after extending for precision");
return (value * r.numerator) / r.denominator;
}

constexpr uint64_t integer_divide_ceil(uint64_t num, uint64_t den ) {
template<typename UnsignedIntType>
constexpr UnsignedIntType integer_divide_ceil(UnsignedIntType num, UnsignedIntType den ) {
return (num / den) + ((num % den) > 0 ? 1 : 0);
}


template<typename LesserIntType, typename GreaterIntType>
constexpr bool is_valid_downgrade_cast =
std::is_integral<LesserIntType>::value && // remove overloads where type is not integral
std::is_integral<GreaterIntType>::value && // remove overloads where type is not integral
(std::numeric_limits<LesserIntType>::max() <= std::numeric_limits<GreaterIntType>::max()); // remove overloads which are upgrades not downgrades

/**
* Specialization for Signedness matching integer types
*/
template<typename LesserIntType, typename GreaterIntType>
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>
{
const GreaterIntType max = std::numeric_limits<LesserIntType>::max();
const GreaterIntType min = std::numeric_limits<LesserIntType>::min();
EOS_ASSERT( val >= min && val <= max, rate_limiting_state_inconsistent, "Casting a higher bit integer value ${v} to a lower bit integer value which cannot contain the value, valid range is [${min}, ${max}]", ("v", val)("min", min)("max",max) );
return LesserIntType(val);
};

/**
* Specialization for Signedness mismatching integer types
*/
template<typename LesserIntType, typename GreaterIntType>
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>
{
const GreaterIntType max = std::numeric_limits<LesserIntType>::max();
const GreaterIntType min = 0;
EOS_ASSERT( val >= min && val <= max, rate_limiting_state_inconsistent, "Casting a higher bit integer value ${v} to a lower bit integer value which cannot contain the value, valid range is [${min}, ${max}]", ("v", val)("min", min)("max",max) );
return LesserIntType(val);
};

/**
* This class accumulates and exponential moving average based on inputs
* This accumulator assumes there are no drops in input data
Expand Down Expand Up @@ -59,7 +94,7 @@ namespace eosio { namespace chain { namespace resource_limits {
EOS_ASSERT(units <= max_raw_value, rate_limiting_state_inconsistent, "Usage exceeds maximum value representable after extending for precision");
EOS_ASSERT(std::numeric_limits<decltype(consumed)>::max() - consumed >= units, rate_limiting_state_inconsistent, "Overflow in tracked usage when adding usage!");

auto value_ex_contrib = integer_divide_ceil(units * Precision, (uint64_t)window_size);
auto value_ex_contrib = downgrade_cast<uint64_t>(integer_divide_ceil((uint128_t)units * Precision, (uint128_t)window_size));
EOS_ASSERT(std::numeric_limits<decltype(value_ex)>::max() - value_ex >= value_ex_contrib, rate_limiting_state_inconsistent, "Overflow in accumulated value when adding usage!");

if( last_ordinal != ordinal ) {
Expand Down
126 changes: 27 additions & 99 deletions libraries/chain/resource_limits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>

if( 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;
auto virtual_network_capacity_in_window = (uint128_t)state.virtual_cpu_limit * window_size;
auto cpu_used_in_window = ((uint128_t)usage.cpu_usage.value_ex * window_size) / (uint128_t)config::rate_limiting_precision;

uint128_t user_weight = cpu_weight;
uint128_t user_weight = (uint128_t)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;
Expand All @@ -128,10 +128,10 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
if( 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;
auto virtual_network_capacity_in_window = (uint128_t)state.virtual_net_limit * window_size;
auto net_used_in_window = ((uint128_t)usage.net_usage.value_ex * window_size) / (uint128_t)config::rate_limiting_precision;

uint128_t user_weight = net_weight;
uint128_t user_weight = (uint128_t)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;
Expand Down Expand Up @@ -335,64 +335,15 @@ uint64_t resource_limits_manager::get_block_net_limit() const {
}

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

const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
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 || state.total_cpu_weight == 0 ) {
return -1;
}

uint128_t window_size = config.account_cpu_usage_average_window;

auto virtual_cpu_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_cpu_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;

/*
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);

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

if( cpu_weight < 0 ) {
return -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;

uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;

if( usable_capacity_ex < consumed_ex ) {
return 0;
}

return (int64_t)((usable_capacity_ex - consumed_ex) / (uint128_t)config::rate_limiting_precision);
*/
auto arl = get_account_cpu_limit_ex(name);
return arl.available;
}

account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const account_name& name ) const {
const auto& config = _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);
const auto& config = _db.get<resource_limits_config_object>();

int64_t cpu_weight, x, y;
get_account_limits( name, x, y, cpu_weight );
Expand All @@ -405,49 +356,26 @@ account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const

uint128_t window_size = config.account_cpu_usage_average_window;

auto virtual_cpu_capacity_in_window = state.virtual_cpu_limit * window_size;
uint128_t user_weight = cpu_weight;
uint128_t all_user_weight = state.total_cpu_weight;
uint128_t virtual_cpu_capacity_in_window = (uint128_t)state.virtual_cpu_limit * window_size;
uint128_t user_weight = (uint128_t)cpu_weight;
uint128_t all_user_weight = (uint128_t)state.total_cpu_weight;

auto max_user_use_in_window = (uint128_t(virtual_cpu_capacity_in_window) * user_weight) / all_user_weight;
auto cpu_used_in_window = (usage.cpu_usage.value_ex * window_size) / config::rate_limiting_precision;
auto max_user_use_in_window = (virtual_cpu_capacity_in_window * user_weight) / all_user_weight;
auto cpu_used_in_window = impl::integer_divide_ceil((uint128_t)usage.cpu_usage.value_ex * window_size, (uint128_t)config::rate_limiting_precision);

if( max_user_use_in_window <= cpu_used_in_window )
arl.available = 0;
else
arl.available = max_user_use_in_window - cpu_used_in_window;

arl.used = cpu_used_in_window;
arl.max = max_user_use_in_window;
arl.available = impl::downgrade_cast<int64_t>(max_user_use_in_window - cpu_used_in_window);

arl.used = impl::downgrade_cast<int64_t>(cpu_used_in_window);
arl.max = impl::downgrade_cast<int64_t>(max_user_use_in_window);
return arl;
}

int64_t resource_limits_manager::get_account_net_limit( const account_name& name ) const {
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
const auto& config = _db.get<resource_limits_config_object>();

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

if( net_weight < 0 || state.total_net_weight == 0 ) {
return -1;
}

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;
auto arl = get_account_net_limit_ex(name);
return arl.available;
}

account_resource_limit resource_limits_manager::get_account_net_limit_ex( const account_name& name ) const {
Expand All @@ -466,21 +394,21 @@ account_resource_limit resource_limits_manager::get_account_net_limit_ex( const

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;
uint128_t virtual_network_capacity_in_window = state.virtual_net_limit * window_size;
uint128_t user_weight = (uint128_t)net_weight;
uint128_t all_user_weight = (uint128_t)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;
auto net_used_in_window = impl::integer_divide_ceil((uint128_t)usage.net_usage.value_ex * window_size, (uint128_t)config::rate_limiting_precision);

if( max_user_use_in_window <= net_used_in_window )
arl.available = 0;
else
arl.available = max_user_use_in_window - net_used_in_window;
arl.available = impl::downgrade_cast<int64_t>(max_user_use_in_window - net_used_in_window);

arl.used = net_used_in_window;
arl.max = max_user_use_in_window;
arl.used = impl::downgrade_cast<int64_t>(net_used_in_window);
arl.max = impl::downgrade_cast<int64_t>(max_user_use_in_window);
return arl;
}

Expand Down