EOCS 最低资源保障机制

为满足普通用户日常的转账等基本需求,无需再为较少的初始资源抵押担心无法使用链上功能。EOCS可以通过链的参数来调整分配给每个用户免费的资源额度,相当于EOCS链上的最低资源保障机制。
系统合约中最低资源保障代码
----------------------------------------------------------------------------------
void system_contract::setmrs( int64_t cpu_us, int64_t net_bytes, int64_t ram_bytes){
require_auth(_self);
set_minimum_resource_security(ram_bytes, net_bytes, cpu_us);
}
其中privileged_api::set_minimum_resource_security是主链提供的供智能合约调用的接口,该函数参数很简单,分别为要设置的cpu,net,和ram的资源,privileged_api::get_resource_limits为获取账户资源状态的接口。函数在头文件eosiolib/privileged.h中声明。
----------------------------------------------------------------------------------
下面为set_minimum_resource_security函数的实现
void set_minimum_resource_security(int64_t ram_bytes, int64_t net_bytes, int64_t cpu_us) {
EOS_ASSERT(cpu_us >= 0, wasm_execution_error, "cpu_us must be >= 0");
EOS_ASSERT(net_bytes >= 0, wasm_execution_error, "net_bytes must be >= 0");
EOS_ASSERT(ram_bytes >= 0, wasm_execution_error, "ram_bytes must be >= 0");
auto& resource_limits = context.control.get_mutable_resource_limits_manager();
int64_t x, y, current_ram_bytes;
resource_limits.get_mrs_parameters(current_ram_bytes, x, y);
EOS_ASSERT(ram_bytes >= current_ram_bytes, wasm_execution_error,
"ram_bytes cannot be reduced, current_ram_bytes: '${current_ram_bytes}', set_ram_bytes: '${set_ram_bytes}'",
("current_ram_bytes", current_ram_bytes)("set_ram_bytes", ram_bytes));
resource_limits.set_mrs_parameters(ram_bytes, net_bytes, cpu_us);
}
----------------------------------------------------------------------------------
函数中通过resource_limits_manager::get_mrs_parameters()和
resource_limits_manager::set_mrs_parameters()分别去获取和设置最低资源保障
最低资源保障定义在resource_limits_config_object类中
class resource_limits_config_object : public chainbase::object<resource_limits_config_object_type, resource_limits_config_object> {
OBJECT_CTOR(resource_limits_config_object);
id_type id;
…
…
…
// Minimal Resource Security (MRS)
int64_t mrs_cpu_us = config::default_mrs_cpu_us;// 200 microseconds
int64_t mrs_net_bytes = config::default_mrs_net_bytes;// 10 KB
int64_t mrs_ram_bytes = config::default_mrs_ram_bytes;// 0 KB
};
----------------------------------------------------------------------------------
设置了最低资源保障后可以通过privileged_api::get_resource_limits->resource_limits_manager::get_account_limits查询
//所以返回的账户资源是否加上最低资源保障根据includes_mrs_ram设定
void resource_limits_manager::get_account_limits( const account_name& account, int64_t& ram_bytes, int64_t& net_weight, int64_t& cpu_weight, bool includes_mrs_ram ) const {
const auto* pending_buo = _db.find<resource_limits_object,by_owner>( boost::make_tuple(true, account) );
const auto& config = _db.get<resource_limits_config_object>();
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;
}
if (includes_mrs_ram) {
ram_bytes += config.mrs_ram_bytes;
}
}
----------------------------------------------------------------------------------
CPU和NET通过抵押EOC获得,每个账户所能获得的资源为:系统总资源* 抵押代币/ 总的抵押代币+ 最低资源保障
// 获取账户当前可用的虚拟CPU
int64_t resource_limits_manager::get_account_cpu_limit( const account_name& name, bool elastic ) const {
auto arl = get_account_cpu_limit_ex(name, elastic);
return arl.available;
}
----------------------------------------------------------------------------------
// 获取账户的虚拟CPU限制
account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const account_name& name, bool elastic) 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 cpu_weight, x, y;
get_account_limits( name, x, y, cpu_weight );
if( cpu_weight < 0 || state.total_cpu_weight == 0 ) {
return { -1, -1, -1 };
}
account_resource_limit arl;
uint128_t window_size = config.account_cpu_usage_average_window;
----------------------------------------------------------------------------------
// 计算窗口期(在这里为24h)内的虚拟计算能力
uint128_t virtual_cpu_capacity_in_window = (uint128_t)(elastic ? state.virtual_cpu_limit : config.cpu_limit_parameters.max) * window_size;
uint128_t user_weight= (uint128_t)cpu_weight;
uint128_t all_user_weight = (uint128_t)state.total_cpu_weight;
----------------------------------------------------------------------------------
// 每个账户所能获得的资源为:系统总资源* 抵押代币/ 总的抵押代币+ 最低资源保障cpu资源
auto max_user_use_in_window = (virtual_cpu_capacity_in_window * user_weight) / all_user_weight + config.mrs_cpu_us;
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 = 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;
}
----------------------------------------------------------------------------------
EOCS对CPU、NET和RAM资源进行统一管理,对系统可用资源和账户可用资源集中记账。
// 将交易消耗的CPU和NET资源计入账户,并且加到块消耗的CPU和NET资源内,通常在交易验证后调用
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 ) {
const auto& state = _db.get<resource_limits_state_object>();
const auto& config = _db.get<resource_limits_config_object>();
for( const auto& a : accounts ) {
const auto& usage = _db.get<resource_usage_object,by_owner>( a );
int64_t unused;
int64_t net_weight;
int64_t cpu_weight;
get_account_limits( a, unused, net_weight, cpu_weight );
_db.modify( usage, [&]( auto& bu ){
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 );
});
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 = (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 = (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 + config.mrs_cpu_us;
EOS_ASSERT( cpu_used_in_window <= max_user_use_in_window,
tx_cpu_usage_exceeded,
"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) );
}
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 = (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 = (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 + config.mrs_net_bytes;
EOS_ASSERT( net_used_in_window <= max_user_use_in_window,
tx_net_usage_exceeded,
"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) );
}
}
// 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" );
}
----------------------------------------------------------------------------------
以上即为EOCS最低资源保障代码分析,此处稍微提一下,EOS引入了虚拟资源,实现动态调节,virtual_block_cpu_limit和virtual_block_net_limit的总资源的初始值分别为block_cpu_limit和block_net_limit,也就是说,虚拟资源一开始等于实际资源,然后随着系统忙闲不断调整,最低值等于实际资源,最高值等于实际资源的1000倍。
详细细节,大家可以下载源码仔细参阅,本期小E为大家带来的EOCS 最低资源保障机制就介绍到这里,欢迎大家关注EOCS官方微信公众号及添加EOCS小秘书共同探讨交流更多技术。

EOCS官网:https://www.eocs.io
GitHub:https://github.com/eocschain
Reddit:https://www.reddit.com/user/eocschain
Twitter:https://twitter.com/eocschain
Telegram:https://t.me/eocschain
网友评论