본 포스팅은 노드원이 B1 자원임대시장 프로포절에 대한 상세 기술적 내용을 담고 있는 Github Pull Request 아티클을 번역한 것입니다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0c120bca-982f-4802-a3be-76768aa326bb/Untitled.png

<aside> 💡 원문 링크: https://github.com/EOSIO/eosio.contracts/pull/397

</aside>

변경 내용 요약

본 PR(풀 리퀘스트)는 시간에 따라 점진적으로 기존 스테이킹 모델과 REX 마켓을 대체할 새로운 CPU/NET 자원 임대 시장을 생성합니다. 기존 모델에서 EOS 토큰 홀더들은 NET과 CPU를 보유하며 이를 스스로 사용하거나, 타인에게 양도하거나, REX 임대시장을 사용하여 타인에게 임대할 수 있었습니다. 하지만 본 신규 자원 임대 모델 하에서는, 체인이 거의 모든 CPU/NET 자원을 직접 소유 및 관리하게 되며, 유저가 이들 자원을 활용하기 위해서는 새로이 도입되는 rentbw 액션을 통하는 방법 밖에는 없습니다. 체인이 유저에게 자원을 임대해 주고 취득한 수수료는 전액 REX 풀로 돌아가, REX에 스테이킹한 유저들이 그 이익을 나눠 갖게 됩니다.

자원 임대

유저가 자원을 임대하기 위해서는 rentbw 액션을 사용:

void rentbw(
    name payer,         // 자원 구매자
    name receiver,      // 자원 수혜자
    uint32_t days,      // 자원 사용 가능 일 수
                        // 시장 설정(market configuration)과 반드시 동일해야함.
    int64_t net_frac,   // 본 시장에 의해 관리되는 전체 net의 비율 (100% = 10^15)
    int64_t cpu_frac,   // 본 시장에 의해 관리되는 전체 cpu의 비율 (100% = 10^15)
    asset max_payment   // 구매자가 지불할 수 있는 최대 토큰 액수
                        // (토큰은 구매자 계정으로부터 지불됨)
);

신규 자원 임대 시장 모델로의 전환이 완료되면, net_frac과 cpu_frac 변수는 네트워크 안의 전체 CPU/NET 리소스 가용량 중 몇 %를 임대할지를 반영합니다. 이를 통해 누가 얼마나 많은 자원을 스테이킹 또는 REX 임대를 통해 자원을 관리하는지 상관 없이 자원 관리를 단순화 할 수 있습니다.

전환기 기간 중에는, net_frac and cpu_frac 가 해당 액션이 호출된 순간, 몇 %의 자원이 신규자원모델 시스템에 의해 관리되는지를 반영합니다. 전환기 기간 시스템에 의해 관리되는 자원의 비중이 점진적으로 늘어나게 될 만큼, 해당 임대 기간중 임대한 리소스 양이 전체 리소스에 대해 차지하는 비중 %는 줄어들게 됩니다.

만료 임대 처리

시스템은 임대기간이 만료된 자원을 자동적으로 회수하지 않습니다. 임대기간이 만료된 자원은 큐(queue)에 머물면서 후속 처리를 기다리게 됩니다. 뒤이어 rentbw 액션을 호출하는 사람이 해당 큐의 처리를 돕게 되는데 (최대 2개 만료 임대 건에 해당 하는 자원 물량분), 이는 임대기간이 만료된 자원이 시간 지체 없이 자동적으로 처리될 수 있도록 하기 위함입니다. 하지만, 상황에 따라 해당 처리를 수동으로 해야 할 경우도 생길 수 있습니다. (시스템 가용 자원을 늘려 가격을 급히 떨어뜨려야 할 경우 등) 이런 경우에는 누구든지(어떤 어카운트든지 상관 없이) rentbwexec 액션을 호출하여 2개 이상의 만료 임대건을 임의대로 처리할 수 있습니다.

설정

BP들은 configrentbw 를 통해 본 자원 임대 시스템을 가동하고, 각종 파라메터들을 현황에 맞게 튜닝해 나갈 수 있습니다. 시스템이 이미 가동중인 동안에라도 언제든 configrentbw 액션을 호출, 실시간 현황에 맞게 각종 파라메터를 조절할 수 있습니다. BP들은 전환기 또는 유예기간 없이 바로 자원의 100%를 자원 임대 모델로 전환해 버릴 수도 있고, 장기간에 걸친 전환기를 가지도록 설정할 수도 있습니다.

// 'rentbw' 마켓을 설정합니다. 
// 자원 임대 시장은 본 액션을 최초 호출시 자동으로 가동됩니다.
void configrentbw( rentbw_config& args );

struct rentbw_config_resource {
   std::optional<int64_t>        current_weight_ratio;   // weight_ratio 값을 이 값으로 즉시 지정. 1x = 10^15. 0.01x = 10^13.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장);
                                                         //    이를 통해  급작스런 가격 변동을 피할 수 있음. 스테이킹 및 REX로부터의 점진적인 
                                                         //    이행이 필요없는 신규 체인은 current_weight_ratio 및 target_weight_ratio로 
                                                         //    0.01x (10^13)를 추천함.
   std::optional<int64_t>        target_weight_ratio;    // weight_ratio를 이 값에 선형적으로 점진 축소 수렴하도록 함. 1x = 10^15. 0.01x = 10^13.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장);
   std::optional<int64_t>        assumed_stake_weight;   // ratio 계산을 위한 잠정 스테이크 weight 값. rentbw 마켓이 최초
                                                         //    활성화되는 시점에서 전체 스테이크 량과 전체 REX 임대량을 합한 값. 
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 없음);
                                                         //    이를 통해  급작스런 가격 변동을 피할 수 있음.
                                                         //    스테이킹 및 REX로부터의 점진적인 이행이 필요없는 신규 체인은 
                                                         //    10^12를 값으로 지정하기를 추천.
   std::optional<time_point_sec> target_timestamp;       // 이 시점에서 weight_ratio 값의 자동 감소를 멈춤. 이 시점이 되면
                                                         //    weight_ratio 값은 target_weight_ratio와 동일해짐. 이 값은
                                                         //    current_weight_ratio == target_weight_ratio 인 조건에서는 무시됨.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 없음);
   std::optional<double>         exponent;               // 가격 곡선의 기울기를 정하기 위한 승수. 1과 같거나 커야함.. 
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장).
   std::optional<uint32_t>       decay_secs;             // 조정 자원 활용도와 현재 자용 활용도 사이의 갭이 63%만큼 줄어드는데 필요한 초 수.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장);
                                                         //    
                                                         //    
   std::optional<asset>          min_price;              // 최소 가격으로 자원 시장 전체를 임대하는 데 필요한 요금.
                                                         //    예를 들어 이는 네트워크 내 전체 토큰량의 0.005%로 설정될 수 있음.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장);
                                                         //    
   std::optional<asset>          max_price;              // 최대 가격으로 자원 시장 전체를 임대하는 데 필요한 요금.
                                                         //    예를 들어 이는 네트워크 내 전체 토큰량의 10%로 설정될 수 있음.
                                                         //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 없음)
                                                         //    
};

struct rentbw_config {
   rentbw_config_resource  net;           // NET 마켓 설정
   rentbw_config_resource  cpu;           // CPU 마켓 설정
   std::optional<uint32_t> rent_days;     // `rentbw` `days` argument는 반드시 이 값과 일치해야 함.
                                          //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 값 유지 권장)
   std::optional<asset>    min_rent_fee;  // 이 가격 이하의 임대료 제안가는 거부.
                                          //    기존 설정을 유지할 목적으로 값을 지정하시 마시오.(디폴트 없음)
};

상기 내용의 이해를 도울 수 있는 유용한 정의들(디폴트 값 포함):

inline constexpr int64_t rentbw_frac = 1'000'000'000'000'000ll;  // 1.0 = 10^15

struct rentbw_state_resource {
   static constexpr double   default_exponent   = 2.0;                  // 지수를 2로 설정시 자원을 소량으로 임대할 시 그 가격이
                                                                        //    네트워크 활용도에 따라 선형적(linearly)으로 올라가게 됨.
                                                                        //    
   static constexpr uint32_t default_decay_secs = 1 * seconds_per_day;  // 1일; 1개 임대건에 자원 대역폭의 100%가 담길 경우,
                                                                        //    더이상의 추가 임대가 없다는 조건 하에, 
                                                                        //    해당 임대건의 만료 시점으로부터 하루가 지나면 
                                                                        //    조정 활용도(adjusted utilization)는 37%가 됨. 
                                                                        //    3일이 지나면 5% 이하.

   uint8_t        version                 = 0;
   int64_t        weight                  = 0;                  // 리소스 마켓 weight(비중). 계속해서 재계산됨(시간에 따른 변동)
                                                                //    1 = 스테이킹된 EOS의 1 사토시 분에 해당하는 리소스 량
                                                                //    
   int64_t        weight_ratio            = 0;                  // 리소스 마켓 weight ratio:
                                                                //    assumed_stake_weight / (assumed_stake_weight + weight).
                                                                //    계속해서 재계산됨(시간에 따른 변동). 1x = 10^15. 0.01x = 10^13.
   int64_t        assumed_stake_weight    = 0;                  // 비율 계산을 위해 잠정하는 assumed stake weight
   int64_t        initial_weight_ratio    = rentbw_frac;        // 선형적 비중 감소를 위해 쓰이는 weight_ratio 초기값.
   int64_t        target_weight_ratio     = rentbw_frac / 100;  // weight_ratio의 선형적 비중감소 목표값.
   time_point_sec initial_timestamp       = {};                 // weight_ratio 감소 시작 시점
   time_point_sec target_timestamp        = {};                 // 이 시점에서 자동적 weight_ratio 비중 감소를 멈춤. 
                                                                //    이 시점에 다다르면 weight_ratio 값은 target_weight_ratio와 동일해짐.
   double         exponent                = default_exponent;   // 자원 가격 곡선을 위한 지수.
   uint32_t       decay_secs              = default_decay_secs; // 조정활용도(adjusted utilization)과 실제활용도(instantaneous utilization)
                                                                //    사이의 갭이 63%가 되는데 필요한 초(sec) 수.
   asset          min_price               = {};                 // 최소 가격으로 자원 시장 전체를 임대하는 데 필요한 요금.
                                                                //    (defaults to 0).
   asset          max_price               = {};                 // 최소 가격으로 자원 시장 전체를 임대하는 데 필요한 요금.
                                                                //    
   int64_t        utilization             = 0;                  // 현재 실제 자원 활용도(Instantaneous resource utilization). 
                                                                //    현재까지 팔린 자원의 양. utilization <= weight.
   int64_t        adjusted_utilization    = 0;                  // 현재 조정 자원 활용도 (Adjusted resource utilization).
                                                                //    이 값은 weight 보다 클 수 없고(<= weight), utilization 보다 항상 크다(>=utilization).
																	                              //    이 값이 커지는 것은 즉각적이지만, 감소는 지수적으로 느리게 일어난다.
   time_point_sec utilization_timestamp   = {};                 // adjusted_utilization이 마지막으로 업데이트된 시점
};

struct rentbw_state {
   static constexpr uint32_t default_rent_days = 30; // 자원 임대기간 30일

   uint8_t                 version      = 0;
   rentbw_state_resource   net          = {};                 // NET 마켓 현황
   rentbw_state_resource   cpu          = {};                 // CPU 마켓 현황
   uint32_t                rent_days    = default_rent_days;  // `rentbw` `days` argument는 반드시 이 값과 일치해야 함.
   asset                   min_rent_fee = {};                 // 이 가격 이하의 임대료 제안가는 거부.
};

배포시 변경 사항

본 신규 자원 모델을 가동하기 전(최초 configrentbw 액션 호출 전)에 반드시 eosio.reserv 어카운트를 새로이 생성해야 합니다. eosio.reserv 어카운트는 그 어떤 액션의 승인자(서명자)로도 작동해서도 안됩니다. 왜냐하면 eosio.reserv 어카운트의 CPU/NET 사용은 configrentbw 와 rentbw 액션의 올바른 작동에 영향을 미칠 수 있기 때문입니다.