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小秘书共同探讨交流更多技术。

 

 

posted @ 2019-04-29 14:31  eocs  阅读(353)  评论(0编辑  收藏  举报