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