From 3872460379f8f3e8ede667d4c5abd8c42e9987c2 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 27 Nov 2019 11:24:28 -0500 Subject: [PATCH 01/45] resource31 --- contracts/eosio.system/CMakeLists.txt | 1 + .../include/eosio.system/eosio.system.hpp | 99 +++++++++++++++++++ contracts/eosio.system/src/resource31.cpp | 31 ++++++ tests/eosio.resource31_tests.cpp | 24 +++++ 4 files changed, 155 insertions(+) create mode 100644 contracts/eosio.system/src/resource31.cpp create mode 100644 tests/eosio.resource31_tests.cpp diff --git a/contracts/eosio.system/CMakeLists.txt b/contracts/eosio.system/CMakeLists.txt index 3f9090846..d8d62b1d2 100644 --- a/contracts/eosio.system/CMakeLists.txt +++ b/contracts/eosio.system/CMakeLists.txt @@ -4,6 +4,7 @@ add_contract(eosio.system eosio.system ${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/resource31.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp ) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index a952edd98..160285989 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -83,6 +83,7 @@ namespace eosiosystem { * - Users can bid on premium names. * - A resource exchange system (REX) allows token holders to lend their tokens, * and users to rent CPU and Network resources in return for a market-determined fee. + * - A resource market separate from REX: `buycpu31`, `buynet31` */ // A name bid, which consists of: @@ -424,6 +425,46 @@ namespace eosiosystem { asset stake_change; }; + enum resource31_type: uint8_t { + cpu = 0, + net = 1, + }; + + struct [[eosio::table,eosio::contract("eosio.system")]] resource31_state { + uint8_t version = 0; + uint8_t type; // resource31_type + int64_t weight; + int64_t initial_weight; + int64_t target_weight; + time_point_sec initial_timestamp; + time_point_sec target_timestamp; + time_point_sec last_update; + double exponent; + uint32_t window; + asset peak_price; + asset min_purchase_price; + int64_t sold; + + uint64_t primary_key()const { return type; } + }; + + typedef eosio::multi_index<"res31.state"_n, resource31_state> resource31_state_table; + + struct [[eosio::table,eosio::contract("eosio.system")]] resource31_order { + uint8_t version = 0; + uint64_t id; + name owner; + uint8_t type; // resource31_type + int64_t weight; + time_point_sec expires; + + uint64_t primary_key()const { return id; } + uint64_t by_owner()const { return owner.value; } + }; + + typedef eosio::multi_index< "res31.order"_n, resource31_order, + indexed_by<"byowner"_n, const_mem_fun>> resource31_order_table; + /** * The EOSIO system contract. The EOSIO system contract governs ram market, voters, producers, global state. */ @@ -1058,6 +1099,60 @@ namespace eosiosystem { [[eosio::action]] void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ); + /** + * Configure or modify the `buycpu31` market. The market becomes available the first time this + * action is invoked. + * + * @param delta_weight - immediately adjust the market cap by this amount. 1 represents the same amount of + * resources as 1 satoshi of SYS staked. + * @param target_weight - linearly grow the market cap to this amount. + * @param target_timestamp - stop automatic market growth at this time. Once this time hits, the market + * cap will be `target_weight`. + */ + [[eosio::action]] + void configcpu31( int64_t delta_weight, int64_t target_weight, const time_point_sec& target_timestamp, + double exponent, uint32_t window, const asset& peak_price, const asset& min_purchase_price ); + + /** + * Configure or modify the `buynet31` market. The market becomes available the first time this + * action is invoked. + * + * @param delta_weight - immediately adjust the market cap by this amount. 1 represents the same amount of + * resources as 1 satoshi of SYS staked. + * @param target_weight - linearly grow the market cap to this amount. + * @param target_timestamp - stop automatic market growth at this time. Once this time hits, the market + * cap will be `target_weight`. + */ + [[eosio::action]] + void confignet31( int64_t delta_weight, int64_t target_weight, const time_point_sec& target_timestamp, + double exponent, uint32_t window, const asset& peak_price, const asset& min_purchase_price ); + + /** + * Buy CPU for 31 days. + * + * @param payer - the resource buyer + * @param receiver - the resource receiver + * @param amount - the amount of resource to buy. 1 has the same amount of resources as + * 1 satoshi of SYS staked. + * @param max_payment - the maximum amount `payer` is willing to pay. Tokens are withdrawn from + * `payer`'s token balance. + */ + [[eosio::action]] + void buycpu31( const name& payer, const name& receiver, int64_t amount, const asset& max_payment ); + + /** + * Buy NET for 31 days. + * + * @param payer - the resource buyer + * @param receiver - the resource receiver + * @param amount - the amount of resource to buy. 1 has the same amount of resources as + * 1 satoshi of SYS staked. + * @param max_payment - the maximum amount `payer` is willing to pay. Tokens are withdrawn from + * `payer`'s token balance. + */ + [[eosio::action]] + void buynet31( const name& payer, const name& receiver, int64_t amount, const asset& max_payment ); + using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; using setacctnet_action = eosio::action_wrapper<"setacctnet"_n, &system_contract::setacctnet>; @@ -1103,6 +1198,10 @@ namespace eosiosystem { using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; + using configcpu31_action = eosio::action_wrapper<"configcpu31"_n, &system_contract::configcpu31>; + using confignet31_action = eosio::action_wrapper<"confignet31"_n, &system_contract::confignet31>; + using buycpu31_action = eosio::action_wrapper<"buycpu31"_n, &system_contract::buycpu31>; + using buynet31_action = eosio::action_wrapper<"buynet31"_n, &system_contract::buynet31>; private: // Implementation details: diff --git a/contracts/eosio.system/src/resource31.cpp b/contracts/eosio.system/src/resource31.cpp new file mode 100644 index 000000000..fc0134a83 --- /dev/null +++ b/contracts/eosio.system/src/resource31.cpp @@ -0,0 +1,31 @@ +#include + +namespace eosiosystem { + +void system_contract::configcpu31(int64_t delta_weight, int64_t target_weight, + const time_point_sec& target_timestamp, + double exponent, uint32_t window, + const asset& peak_price, + const asset& min_purchase_price) { + // +} + +void system_contract::confignet31(int64_t delta_weight, int64_t target_weight, + const time_point_sec& target_timestamp, + double exponent, uint32_t window, + const asset& peak_price, + const asset& min_purchase_price) { + // +} + +void system_contract::buycpu31(const name& payer, const name& receiver, + int64_t amount, const asset& max_payment) { + // +} + +void system_contract::buynet31(const name& payer, const name& receiver, + int64_t amount, const asset& max_payment) { + // +} + +} // namespace eosiosystem diff --git a/tests/eosio.resource31_tests.cpp b/tests/eosio.resource31_tests.cpp new file mode 100644 index 000000000..b25261a7e --- /dev/null +++ b/tests/eosio.resource31_tests.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eosio.system_tester.hpp" + +using namespace eosio_system; + +BOOST_AUTO_TEST_SUITE(eosio_system_resource31_tests) + +BOOST_FIXTURE_TEST_CASE(foo, eosio_system_tester) try { + // +} +FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() From 71830da94e90130dccb1a8258844488358ad4946 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 27 Nov 2019 16:00:30 -0500 Subject: [PATCH 02/45] buybandwidth --- contracts/eosio.system/CMakeLists.txt | 2 +- .../include/eosio.system/eosio.system.hpp | 121 ++++++++---------- contracts/eosio.system/src/buybandwidth.cpp | 14 ++ contracts/eosio.system/src/resource31.cpp | 31 ----- ...tests.cpp => eosio.buybandwidth_tests.cpp} | 4 +- 5 files changed, 69 insertions(+), 103 deletions(-) create mode 100644 contracts/eosio.system/src/buybandwidth.cpp delete mode 100644 contracts/eosio.system/src/resource31.cpp rename tests/{eosio.resource31_tests.cpp => eosio.buybandwidth_tests.cpp} (90%) diff --git a/contracts/eosio.system/CMakeLists.txt b/contracts/eosio.system/CMakeLists.txt index d8d62b1d2..825a02d75 100644 --- a/contracts/eosio.system/CMakeLists.txt +++ b/contracts/eosio.system/CMakeLists.txt @@ -1,10 +1,10 @@ add_contract(eosio.system eosio.system + ${CMAKE_CURRENT_SOURCE_DIR}/src/buybandwidth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/resource31.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp ) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 160285989..dae19ef16 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -83,7 +83,7 @@ namespace eosiosystem { * - Users can bid on premium names. * - A resource exchange system (REX) allows token holders to lend their tokens, * and users to rent CPU and Network resources in return for a market-determined fee. - * - A resource market separate from REX: `buycpu31`, `buynet31` + * - A resource market separate from REX: `buybandwidth` */ // A name bid, which consists of: @@ -425,45 +425,62 @@ namespace eosiosystem { asset stake_change; }; - enum resource31_type: uint8_t { - cpu = 0, - net = 1, + struct buybw_config_resource { + int64_t current_weight; // Immediately set the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. + int64_t target_weight; // Linearly grow the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. + time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. + // Ignored if current_weight == target_weight. + double exponent; // Exponent of resource price curve. Must be >= 1. + uint32_t decay_days; // Number of days for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + asset total_price; // Total price needed to buy the entire resource market weight. + asset min_purchase_price; // Minimum purchase. This needs to be large enough to cover RAM costs. }; - struct [[eosio::table,eosio::contract("eosio.system")]] resource31_state { + struct buybw_config { + buybw_config_resource net; // NET market configuration + buybw_config_resource cpu; // CPU market configuration + uint32_t purchase_days; // `buybandwidth` `days` argument must match this. + }; + + struct buybw_state_resource { + uint8_t version = 0; + int64_t weight; // resource market weight + int64_t initial_weight; // Initial resource market weight used for linear growth + int64_t target_weight; // Linearly grow the resource market weight to this amount + time_point_sec initial_timestamp; // When resource market weight growth started + time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. + double exponent; // Exponent of resource price curve. + uint32_t decay_days; // Number of days for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + asset total_price; // Total price needed to buy the entire resource market weight. + asset min_purchase_price; // Minimum purchase + int64_t utilization; // Instantaneous resource utilization. This is the current amount sold. + int64_t adjusted_utilization; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. + }; + + struct [[eosio::table("buybw.state"),eosio::contract("eosio.system")]] buybw_state { uint8_t version = 0; - uint8_t type; // resource31_type - int64_t weight; - int64_t initial_weight; - int64_t target_weight; - time_point_sec initial_timestamp; - time_point_sec target_timestamp; - time_point_sec last_update; - double exponent; - uint32_t window; - asset peak_price; - asset min_purchase_price; - int64_t sold; - - uint64_t primary_key()const { return type; } + buybw_state_resource net; // NET market state + buybw_state_resource cpu; // CPU market state + + uint64_t primary_key()const { return 0; } }; - typedef eosio::multi_index<"res31.state"_n, resource31_state> resource31_state_table; + typedef eosio::singleton<"buybw.state"_n, buybw_state> buybw_state_singleton; - struct [[eosio::table,eosio::contract("eosio.system")]] resource31_order { + struct [[eosio::table("buybw.order"),eosio::contract("eosio.system")]] buybw_order { uint8_t version = 0; uint64_t id; name owner; - uint8_t type; // resource31_type - int64_t weight; + int64_t net_weight; + int64_t cpu_weight; time_point_sec expires; uint64_t primary_key()const { return id; } uint64_t by_owner()const { return owner.value; } }; - typedef eosio::multi_index< "res31.order"_n, resource31_order, - indexed_by<"byowner"_n, const_mem_fun>> resource31_order_table; + typedef eosio::multi_index< "buybw.order"_n, buybw_order, + indexed_by<"byowner"_n, const_mem_fun>> buybw_order_table; /** * The EOSIO system contract. The EOSIO system contract governs ram market, voters, producers, global state. @@ -499,6 +516,7 @@ namespace eosiosystem { static constexpr eosio::name names_account{"eosio.names"_n}; static constexpr eosio::name saving_account{"eosio.saving"_n}; static constexpr eosio::name rex_account{"eosio.rex"_n}; + static constexpr eosio::name reserv_account{"eosio.reserv"_n}; static constexpr eosio::name null_account{"eosio.null"_n}; static constexpr symbol ramcore_symbol = symbol(symbol_code("RAMCORE"), 4); static constexpr symbol ram_symbol = symbol(symbol_code("RAM"), 0); @@ -1100,58 +1118,25 @@ namespace eosiosystem { void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ); /** - * Configure or modify the `buycpu31` market. The market becomes available the first time this - * action is invoked. - * - * @param delta_weight - immediately adjust the market cap by this amount. 1 represents the same amount of - * resources as 1 satoshi of SYS staked. - * @param target_weight - linearly grow the market cap to this amount. - * @param target_timestamp - stop automatic market growth at this time. Once this time hits, the market - * cap will be `target_weight`. - */ - [[eosio::action]] - void configcpu31( int64_t delta_weight, int64_t target_weight, const time_point_sec& target_timestamp, - double exponent, uint32_t window, const asset& peak_price, const asset& min_purchase_price ); - - /** - * Configure or modify the `buynet31` market. The market becomes available the first time this + * Configure the `buybandwidth` market. The market becomes available the first time this * action is invoked. - * - * @param delta_weight - immediately adjust the market cap by this amount. 1 represents the same amount of - * resources as 1 satoshi of SYS staked. - * @param target_weight - linearly grow the market cap to this amount. - * @param target_timestamp - stop automatic market growth at this time. Once this time hits, the market - * cap will be `target_weight`. - */ - [[eosio::action]] - void confignet31( int64_t delta_weight, int64_t target_weight, const time_point_sec& target_timestamp, - double exponent, uint32_t window, const asset& peak_price, const asset& min_purchase_price ); - - /** - * Buy CPU for 31 days. - * - * @param payer - the resource buyer - * @param receiver - the resource receiver - * @param amount - the amount of resource to buy. 1 has the same amount of resources as - * 1 satoshi of SYS staked. - * @param max_payment - the maximum amount `payer` is willing to pay. Tokens are withdrawn from - * `payer`'s token balance. */ [[eosio::action]] - void buycpu31( const name& payer, const name& receiver, int64_t amount, const asset& max_payment ); + void configbuybw(const buybw_config& args); /** - * Buy NET for 31 days. + * Buy NET and CPU * * @param payer - the resource buyer * @param receiver - the resource receiver - * @param amount - the amount of resource to buy. 1 has the same amount of resources as - * 1 satoshi of SYS staked. + * @param days - number of days of resource availability. Must match market configuration. + * @param net - fraction of net (100% = 10^18) managed by this market + * @param cpu - fraction of cpu (100% = 10^18) managed by this market * @param max_payment - the maximum amount `payer` is willing to pay. Tokens are withdrawn from * `payer`'s token balance. */ [[eosio::action]] - void buynet31( const name& payer, const name& receiver, int64_t amount, const asset& max_payment ); + void buybandwidth( const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, const asset& max_payment ); using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; @@ -1198,10 +1183,8 @@ namespace eosiosystem { using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; - using configcpu31_action = eosio::action_wrapper<"configcpu31"_n, &system_contract::configcpu31>; - using confignet31_action = eosio::action_wrapper<"confignet31"_n, &system_contract::confignet31>; - using buycpu31_action = eosio::action_wrapper<"buycpu31"_n, &system_contract::buycpu31>; - using buynet31_action = eosio::action_wrapper<"buynet31"_n, &system_contract::buynet31>; + using configcpu_action = eosio::action_wrapper<"configbuybw"_n, &system_contract::configbuybw>; + using buybandwidth_action = eosio::action_wrapper<"buybandwidth"_n, &system_contract::buybandwidth>; private: // Implementation details: diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/buybandwidth.cpp new file mode 100644 index 000000000..c25e5b7df --- /dev/null +++ b/contracts/eosio.system/src/buybandwidth.cpp @@ -0,0 +1,14 @@ +#include + +namespace eosiosystem { + +void system_contract::configbuybw(const buybw_config& args) { + // +} + +void system_contract::buybandwidth(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, + const asset& max_payment) { + // +} + +} // namespace eosiosystem diff --git a/contracts/eosio.system/src/resource31.cpp b/contracts/eosio.system/src/resource31.cpp deleted file mode 100644 index fc0134a83..000000000 --- a/contracts/eosio.system/src/resource31.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include - -namespace eosiosystem { - -void system_contract::configcpu31(int64_t delta_weight, int64_t target_weight, - const time_point_sec& target_timestamp, - double exponent, uint32_t window, - const asset& peak_price, - const asset& min_purchase_price) { - // -} - -void system_contract::confignet31(int64_t delta_weight, int64_t target_weight, - const time_point_sec& target_timestamp, - double exponent, uint32_t window, - const asset& peak_price, - const asset& min_purchase_price) { - // -} - -void system_contract::buycpu31(const name& payer, const name& receiver, - int64_t amount, const asset& max_payment) { - // -} - -void system_contract::buynet31(const name& payer, const name& receiver, - int64_t amount, const asset& max_payment) { - // -} - -} // namespace eosiosystem diff --git a/tests/eosio.resource31_tests.cpp b/tests/eosio.buybandwidth_tests.cpp similarity index 90% rename from tests/eosio.resource31_tests.cpp rename to tests/eosio.buybandwidth_tests.cpp index b25261a7e..41ecadcb9 100644 --- a/tests/eosio.resource31_tests.cpp +++ b/tests/eosio.buybandwidth_tests.cpp @@ -14,10 +14,10 @@ using namespace eosio_system; -BOOST_AUTO_TEST_SUITE(eosio_system_resource31_tests) +BOOST_AUTO_TEST_SUITE(eosio_system_buybandwidth_tests) BOOST_FIXTURE_TEST_CASE(foo, eosio_system_tester) try { - // + // } FC_LOG_AND_RETHROW() From f82ab38dd981f0e96bfc93d4052db69fb64ba030 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 27 Nov 2019 17:33:02 -0500 Subject: [PATCH 03/45] buybandwidth --- .../include/eosio.system/eosio.system.hpp | 35 ++++++++--------- contracts/eosio.system/src/buybandwidth.cpp | 38 ++++++++++++++++++- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index dae19ef16..f23850626 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -431,7 +431,7 @@ namespace eosiosystem { time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. // Ignored if current_weight == target_weight. double exponent; // Exponent of resource price curve. Must be >= 1. - uint32_t decay_days; // Number of days for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). asset total_price; // Total price needed to buy the entire resource market weight. asset min_purchase_price; // Minimum purchase. This needs to be large enough to cover RAM costs. }; @@ -443,24 +443,25 @@ namespace eosiosystem { }; struct buybw_state_resource { - uint8_t version = 0; - int64_t weight; // resource market weight - int64_t initial_weight; // Initial resource market weight used for linear growth - int64_t target_weight; // Linearly grow the resource market weight to this amount - time_point_sec initial_timestamp; // When resource market weight growth started - time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. - double exponent; // Exponent of resource price curve. - uint32_t decay_days; // Number of days for adjusted resource utilization to decay to instantaneous utilization within exp(-1). - asset total_price; // Total price needed to buy the entire resource market weight. - asset min_purchase_price; // Minimum purchase - int64_t utilization; // Instantaneous resource utilization. This is the current amount sold. - int64_t adjusted_utilization; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. + uint8_t version = 0; + int64_t weight = 0; // resource market weight + int64_t initial_weight = 0; // Initial resource market weight used for linear growth + int64_t target_weight = 0; // Linearly grow the resource market weight to this amount + time_point_sec initial_timestamp = {}; // When resource market weight growth started + time_point_sec target_timestamp = {}; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. + double exponent = 0; // Exponent of resource price curve. + uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + asset total_price = {}; // Total price needed to buy the entire resource market weight. + asset min_purchase_price = {}; // Minimum purchase + int64_t utilization = 0; // Instantaneous resource utilization. This is the current amount sold. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. }; struct [[eosio::table("buybw.state"),eosio::contract("eosio.system")]] buybw_state { - uint8_t version = 0; - buybw_state_resource net; // NET market state - buybw_state_resource cpu; // CPU market state + uint8_t version = 0; + buybw_state_resource net = {}; // NET market state + buybw_state_resource cpu = {}; // CPU market state + uint32_t purchase_days = 0; // `buybandwidth` `days` argument must match this. uint64_t primary_key()const { return 0; } }; @@ -1122,7 +1123,7 @@ namespace eosiosystem { * action is invoked. */ [[eosio::action]] - void configbuybw(const buybw_config& args); + void configbuybw(buybw_config& args); /** * Buy NET and CPU diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/buybandwidth.cpp index c25e5b7df..f994e482a 100644 --- a/contracts/eosio.system/src/buybandwidth.cpp +++ b/contracts/eosio.system/src/buybandwidth.cpp @@ -2,8 +2,42 @@ namespace eosiosystem { -void system_contract::configbuybw(const buybw_config& args) { - // +void system_contract::configbuybw(buybw_config& args) { + time_point_sec now = eosio::current_time_point(); + auto core_symbol = get_core_symbol(); + buybw_state_singleton state_sing{ get_self(), 0 }; + auto state = state_sing.get_or_default(); + + auto update = [&](auto& state, auto& args) { + if (args.current_weight == args.target_weight) + args.target_timestamp = now; + else + eosio::check(args.target_timestamp > now, "target_timestamp must be in the future"); + eosio::check(args.target_weight >= args.current_weight, "weight can't shrink over time"); + eosio::check(args.current_weight >= state.utilization, "weight can't shrink below utilization"); + eosio::check(args.exponent >= 1, "exponent must be >= 1"); + eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); + eosio::check(args.total_price.symbol == core_symbol, "total_price doesn't match core symbol"); + eosio::check(args.total_price.amount > 0, "total_price must be positive"); + eosio::check(args.min_purchase_price.symbol == core_symbol, "min_purchase_price doesn't match core symbol"); + + state.weight = args.current_weight; + state.initial_weight = args.current_weight; + state.target_weight = args.target_weight; + state.initial_timestamp = now; + state.target_timestamp = args.target_timestamp; + state.exponent = args.exponent; + state.decay_secs = args.decay_secs; + state.total_price = args.total_price; + state.min_purchase_price = args.min_purchase_price; + }; + + eosio::check(args.purchase_days > 0, "purchase_days must be > 0"); + update(state.net, args.net); + update(state.cpu, args.cpu); + state.purchase_days = args.purchase_days; + + state_sing.set(state, get_self()); } void system_contract::buybandwidth(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, From 10121804d125bd63ce6e3f83176ae65b3e5f9c95 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 27 Nov 2019 18:03:34 -0500 Subject: [PATCH 04/45] buybandwidth --- .../include/eosio.system/eosio.system.hpp | 3 + contracts/eosio.system/src/buybandwidth.cpp | 63 ++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index f23850626..c5b77ec06 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -1283,6 +1283,9 @@ namespace eosiosystem { }; registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; + + // defined in buybandwidth.cpp + void adjust_resources(name payer, name account, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); }; } diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/buybandwidth.cpp index f994e482a..3126b7d14 100644 --- a/contracts/eosio.system/src/buybandwidth.cpp +++ b/contracts/eosio.system/src/buybandwidth.cpp @@ -2,13 +2,64 @@ namespace eosiosystem { +void system_contract::adjust_resources(name payer, name account, int64_t net_delta, int64_t cpu_delta, + bool must_not_be_managed) { + if (!net_delta && !cpu_delta) + return; + + user_resources_table totals_tbl(get_self(), account.value); + auto tot_itr = totals_tbl.find(account.value); + if (tot_itr == totals_tbl.end()) { + tot_itr = totals_tbl.emplace(payer, [&](auto& tot) { + tot.owner = account; + tot.net_weight = net_delta; + tot.cpu_weight = cpu_delta; + }); + } else { + totals_tbl.modify(tot_itr, same_payer, [&](auto& tot) { + tot.net_weight += net_delta; + tot.cpu_weight += cpu_delta; + }); + } + check(0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth"); + check(0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth"); + + { + bool ram_managed = false; + bool net_managed = false; + bool cpu_managed = false; + + auto voter_itr = _voters.find(account.value); + if (voter_itr != _voters.end()) { + ram_managed = has_field(voter_itr->flags1, voter_info::flags1_fields::ram_managed); + net_managed = has_field(voter_itr->flags1, voter_info::flags1_fields::net_managed); + cpu_managed = has_field(voter_itr->flags1, voter_info::flags1_fields::cpu_managed); + } + + if (must_not_be_managed) + eosio::check(!net_managed && !cpu_managed, "something is managed which shouldn't be"); + + if (!(net_managed && cpu_managed)) { + int64_t ram_bytes, net, cpu; + get_resource_limits(account, ram_bytes, net, cpu); + set_resource_limits( + account, ram_managed ? ram_bytes : std::max(tot_itr->ram_bytes + ram_gift_bytes, ram_bytes), + net_managed ? net : tot_itr->net_weight.amount, cpu_managed ? cpu : tot_itr->cpu_weight.amount); + } + } + + if (tot_itr->is_empty()) { + totals_tbl.erase(tot_itr); + } +} // system_contract::adjust_resources + void system_contract::configbuybw(buybw_config& args) { time_point_sec now = eosio::current_time_point(); auto core_symbol = get_core_symbol(); buybw_state_singleton state_sing{ get_self(), 0 }; auto state = state_sing.get_or_default(); - auto update = [&](auto& state, auto& args) { + auto update = [&](auto& state, auto& args, auto& delta_weight) { if (args.current_weight == args.target_weight) args.target_timestamp = now; else @@ -21,6 +72,8 @@ void system_contract::configbuybw(buybw_config& args) { eosio::check(args.total_price.amount > 0, "total_price must be positive"); eosio::check(args.min_purchase_price.symbol == core_symbol, "min_purchase_price doesn't match core symbol"); + delta_weight = args.current_weight - state.weight; + state.weight = args.current_weight; state.initial_weight = args.current_weight; state.target_weight = args.target_weight; @@ -33,10 +86,14 @@ void system_contract::configbuybw(buybw_config& args) { }; eosio::check(args.purchase_days > 0, "purchase_days must be > 0"); - update(state.net, args.net); - update(state.cpu, args.cpu); state.purchase_days = args.purchase_days; + int64_t net_delta = 0; + int64_t cpu_delta = 0; + update(state.net, args.net, net_delta); + update(state.cpu, args.cpu, cpu_delta); + + adjust_resources(get_self(), reserv_account, net_delta, cpu_delta, true); state_sing.set(state, get_self()); } From 0c8d84c405b3eddc1eb61a7461ec780193804b51 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 27 Nov 2019 18:56:26 -0500 Subject: [PATCH 05/45] buybandwidth --- .../include/eosio.system/eosio.system.hpp | 9 +++- contracts/eosio.system/src/buybandwidth.cpp | 51 +++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index c5b77ec06..08266826e 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -478,10 +478,13 @@ namespace eosiosystem { uint64_t primary_key()const { return id; } uint64_t by_owner()const { return owner.value; } + uint64_t by_expires()const { return expires.utc_seconds; } }; typedef eosio::multi_index< "buybw.order"_n, buybw_order, - indexed_by<"byowner"_n, const_mem_fun>> buybw_order_table; + indexed_by<"byowner"_n, const_mem_fun>, + indexed_by<"byexpires"_n, const_mem_fun> + > buybw_order_table; /** * The EOSIO system contract. The EOSIO system contract governs ram market, voters, producers, global state. @@ -1285,7 +1288,9 @@ namespace eosiosystem { registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; // defined in buybandwidth.cpp - void adjust_resources(name payer, name account, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); + void adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); + void process_buybw_queue(symbol core_symbol, buybw_state& state, buybw_order_table& orders, uint32_t max_items); + void update_buybw_state(buybw_state& state); }; } diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/buybandwidth.cpp index 3126b7d14..2f4d1a436 100644 --- a/contracts/eosio.system/src/buybandwidth.cpp +++ b/contracts/eosio.system/src/buybandwidth.cpp @@ -2,8 +2,8 @@ namespace eosiosystem { -void system_contract::adjust_resources(name payer, name account, int64_t net_delta, int64_t cpu_delta, - bool must_not_be_managed) { +void system_contract::adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, + int64_t cpu_delta, bool must_not_be_managed) { if (!net_delta && !cpu_delta) return; @@ -12,13 +12,13 @@ void system_contract::adjust_resources(name payer, name account, int64_t net_del if (tot_itr == totals_tbl.end()) { tot_itr = totals_tbl.emplace(payer, [&](auto& tot) { tot.owner = account; - tot.net_weight = net_delta; - tot.cpu_weight = cpu_delta; + tot.net_weight = asset{ net_delta, core_symbol }; + tot.cpu_weight = asset{ cpu_delta, core_symbol }; }); } else { totals_tbl.modify(tot_itr, same_payer, [&](auto& tot) { - tot.net_weight += net_delta; - tot.cpu_weight += cpu_delta; + tot.net_weight.amount += net_delta; + tot.cpu_weight.amount += cpu_delta; }); } check(0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth"); @@ -53,7 +53,32 @@ void system_contract::adjust_resources(name payer, name account, int64_t net_del } } // system_contract::adjust_resources +void system_contract::process_buybw_queue(symbol core_symbol, buybw_state& state, buybw_order_table& orders, + uint32_t max_items) { + time_point_sec now = eosio::current_time_point(); + auto idx = orders.get_index<"byexpires"_n>(); + int64_t total_net = 0; + int64_t total_cpu = 0; + while (max_items--) { + auto it = idx.begin(); + if (it == idx.end() || it->expires > now) + break; + total_net = it->net_weight; + total_cpu = it->cpu_weight; + adjust_resources(get_self(), it->owner, core_symbol, -it->net_weight, -it->cpu_weight); + idx.erase(it); + } + state.net.utilization -= total_net; + state.cpu.utilization -= total_cpu; + adjust_resources(get_self(), reserv_account, core_symbol, total_net, total_cpu, true); +} + +void system_contract::update_buybw_state(buybw_state& state) { + // +} + void system_contract::configbuybw(buybw_config& args) { + require_auth(get_self()); time_point_sec now = eosio::current_time_point(); auto core_symbol = get_core_symbol(); buybw_state_singleton state_sing{ get_self(), 0 }; @@ -93,13 +118,21 @@ void system_contract::configbuybw(buybw_config& args) { update(state.net, args.net, net_delta); update(state.cpu, args.cpu, cpu_delta); - adjust_resources(get_self(), reserv_account, net_delta, cpu_delta, true); + adjust_resources(get_self(), reserv_account, core_symbol, net_delta, cpu_delta, true); state_sing.set(state, get_self()); -} +} // system_contract::configbuybw void system_contract::buybandwidth(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, const asset& max_payment) { - // + require_auth(payer); + buybw_state_singleton state_sing{ get_self(), 0 }; + buybw_order_table orders{ get_self(), 0 }; + eosio::check(state_sing.exists(), "buybandwidth hasn't been initialized"); + auto state = state_sing.get(); + auto core_symbol = get_core_symbol(); + process_buybw_queue(core_symbol, state, orders, 2); + update_buybw_state(state); + state_sing.set(state, get_self()); } } // namespace eosiosystem From 4d296dfdedb5cfd12a2c624c4671587c1c99d448 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 2 Dec 2019 11:14:40 -0500 Subject: [PATCH 06/45] buybandwidth --- .../include/eosio.system/eosio.system.hpp | 33 ++++++++++--------- contracts/eosio.system/src/buybandwidth.cpp | 28 +++++++++++++++- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 08266826e..e2f4b87a9 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -443,25 +443,26 @@ namespace eosiosystem { }; struct buybw_state_resource { - uint8_t version = 0; - int64_t weight = 0; // resource market weight - int64_t initial_weight = 0; // Initial resource market weight used for linear growth - int64_t target_weight = 0; // Linearly grow the resource market weight to this amount - time_point_sec initial_timestamp = {}; // When resource market weight growth started - time_point_sec target_timestamp = {}; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. - double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). - asset total_price = {}; // Total price needed to buy the entire resource market weight. - asset min_purchase_price = {}; // Minimum purchase - int64_t utilization = 0; // Instantaneous resource utilization. This is the current amount sold. - int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. + uint8_t version = 0; + int64_t weight = 0; // resource market weight + int64_t initial_weight = 0; // Initial resource market weight used for linear growth + int64_t target_weight = 0; // Linearly grow the resource market weight to this amount + time_point_sec initial_timestamp = {}; // When resource market weight growth started + time_point_sec target_timestamp = {}; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. + double exponent = 0; // Exponent of resource price curve. + uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + asset total_price = {}; // Total price needed to buy the entire resource market weight. + asset min_purchase_price = {}; // Minimum purchase + int64_t utilization = 0; // Instantaneous resource utilization. This is the current amount sold. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. + time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; struct [[eosio::table("buybw.state"),eosio::contract("eosio.system")]] buybw_state { - uint8_t version = 0; - buybw_state_resource net = {}; // NET market state - buybw_state_resource cpu = {}; // CPU market state - uint32_t purchase_days = 0; // `buybandwidth` `days` argument must match this. + uint8_t version = 0; + buybw_state_resource net = {}; // NET market state + buybw_state_resource cpu = {}; // CPU market state + uint32_t purchase_days = 0; // `buybandwidth` `days` argument must match this. uint64_t primary_key()const { return 0; } }; diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/buybandwidth.cpp index 2f4d1a436..eae4abb78 100644 --- a/contracts/eosio.system/src/buybandwidth.cpp +++ b/contracts/eosio.system/src/buybandwidth.cpp @@ -1,4 +1,5 @@ #include +#include namespace eosiosystem { @@ -73,8 +74,33 @@ void system_contract::process_buybw_queue(symbol core_symbol, buybw_state& state adjust_resources(get_self(), reserv_account, core_symbol, total_net, total_cpu, true); } +void update_weight(time_point_sec now, buybw_state_resource& res) { + if (now >= res.target_timestamp) + res.weight = res.target_weight; + else + res.weight = res.initial_weight + // + int128_t(res.target_weight - res.initial_weight) * + (now.utc_seconds - res.initial_timestamp.utc_seconds) / + (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); +} + +void update_utilization(time_point_sec now, buybw_state_resource& res) { + if (res.utilization >= res.adjusted_utilization) + res.adjusted_utilization = res.utilization; + else + res.adjusted_utilization = // + res.utilization + + (res.adjusted_utilization - res.utilization) * + exp((now.utc_seconds - res.utilization_timestamp.utc_seconds) / double(-res.decay_secs)); + res.utilization_timestamp = now; +} + void system_contract::update_buybw_state(buybw_state& state) { - // + time_point_sec now = eosio::current_time_point(); + update_weight(now, state.net); + update_weight(now, state.cpu); + update_utilization(now, state.net); + update_utilization(now, state.cpu); } void system_contract::configbuybw(buybw_config& args) { From a73a7ff5b2403148767ab32c41108db7d04fd8dc Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 2 Dec 2019 11:54:25 -0500 Subject: [PATCH 07/45] rentbw --- contracts/eosio.system/CMakeLists.txt | 2 +- .../include/eosio.system/eosio.system.hpp | 58 +++++++++--------- .../src/{buybandwidth.cpp => rentbw.cpp} | 60 +++++++++---------- ...width_tests.cpp => eosio.rentbw_tests.cpp} | 2 +- 4 files changed, 61 insertions(+), 61 deletions(-) rename contracts/eosio.system/src/{buybandwidth.cpp => rentbw.cpp} (73%) rename tests/{eosio.buybandwidth_tests.cpp => eosio.rentbw_tests.cpp} (90%) diff --git a/contracts/eosio.system/CMakeLists.txt b/contracts/eosio.system/CMakeLists.txt index 825a02d75..33f7d486e 100644 --- a/contracts/eosio.system/CMakeLists.txt +++ b/contracts/eosio.system/CMakeLists.txt @@ -1,10 +1,10 @@ add_contract(eosio.system eosio.system - ${CMAKE_CURRENT_SOURCE_DIR}/src/buybandwidth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/rentbw.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp ) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index e2f4b87a9..5fc33bcdc 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -83,7 +83,7 @@ namespace eosiosystem { * - Users can bid on premium names. * - A resource exchange system (REX) allows token holders to lend their tokens, * and users to rent CPU and Network resources in return for a market-determined fee. - * - A resource market separate from REX: `buybandwidth` + * - A resource market separate from REX: `rentbw` */ // A name bid, which consists of: @@ -425,7 +425,7 @@ namespace eosiosystem { asset stake_change; }; - struct buybw_config_resource { + struct rentbw_config_resource { int64_t current_weight; // Immediately set the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. int64_t target_weight; // Linearly grow the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. @@ -433,16 +433,16 @@ namespace eosiosystem { double exponent; // Exponent of resource price curve. Must be >= 1. uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). asset total_price; // Total price needed to buy the entire resource market weight. - asset min_purchase_price; // Minimum purchase. This needs to be large enough to cover RAM costs. + asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover RAM costs. }; - struct buybw_config { - buybw_config_resource net; // NET market configuration - buybw_config_resource cpu; // CPU market configuration - uint32_t purchase_days; // `buybandwidth` `days` argument must match this. + struct rentbw_config { + rentbw_config_resource net; // NET market configuration + rentbw_config_resource cpu; // CPU market configuration + uint32_t rent_days; // `rentbw` `days` argument must match this. }; - struct buybw_state_resource { + struct rentbw_state_resource { uint8_t version = 0; int64_t weight = 0; // resource market weight int64_t initial_weight = 0; // Initial resource market weight used for linear growth @@ -452,24 +452,24 @@ namespace eosiosystem { double exponent = 0; // Exponent of resource price curve. uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). asset total_price = {}; // Total price needed to buy the entire resource market weight. - asset min_purchase_price = {}; // Minimum purchase + asset min_rent_price = {}; // Rents below this amount are rejected int64_t utilization = 0; // Instantaneous resource utilization. This is the current amount sold. int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; - struct [[eosio::table("buybw.state"),eosio::contract("eosio.system")]] buybw_state { - uint8_t version = 0; - buybw_state_resource net = {}; // NET market state - buybw_state_resource cpu = {}; // CPU market state - uint32_t purchase_days = 0; // `buybandwidth` `days` argument must match this. + struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { + uint8_t version = 0; + rentbw_state_resource net = {}; // NET market state + rentbw_state_resource cpu = {}; // CPU market state + uint32_t rent_days = 0; // `rentbw` `days` argument must match this. uint64_t primary_key()const { return 0; } }; - typedef eosio::singleton<"buybw.state"_n, buybw_state> buybw_state_singleton; + typedef eosio::singleton<"rent.state"_n, rentbw_state> rentbw_state_singleton; - struct [[eosio::table("buybw.order"),eosio::contract("eosio.system")]] buybw_order { + struct [[eosio::table("rentbw.order"),eosio::contract("eosio.system")]] rentbw_order { uint8_t version = 0; uint64_t id; name owner; @@ -482,10 +482,10 @@ namespace eosiosystem { uint64_t by_expires()const { return expires.utc_seconds; } }; - typedef eosio::multi_index< "buybw.order"_n, buybw_order, - indexed_by<"byowner"_n, const_mem_fun>, - indexed_by<"byexpires"_n, const_mem_fun> - > buybw_order_table; + typedef eosio::multi_index< "rentbw.order"_n, rentbw_order, + indexed_by<"byowner"_n, const_mem_fun>, + indexed_by<"byexpires"_n, const_mem_fun> + > rentbw_order_table; /** * The EOSIO system contract. The EOSIO system contract governs ram market, voters, producers, global state. @@ -1123,14 +1123,14 @@ namespace eosiosystem { void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ); /** - * Configure the `buybandwidth` market. The market becomes available the first time this + * Configure the `rentbw` market. The market becomes available the first time this * action is invoked. */ [[eosio::action]] - void configbuybw(buybw_config& args); + void configrentbw(rentbw_config& args); /** - * Buy NET and CPU + * Rent NET and CPU * * @param payer - the resource buyer * @param receiver - the resource receiver @@ -1141,7 +1141,7 @@ namespace eosiosystem { * `payer`'s token balance. */ [[eosio::action]] - void buybandwidth( const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, const asset& max_payment ); + void rentbw( const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, const asset& max_payment ); using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; @@ -1188,8 +1188,8 @@ namespace eosiosystem { using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; - using configcpu_action = eosio::action_wrapper<"configbuybw"_n, &system_contract::configbuybw>; - using buybandwidth_action = eosio::action_wrapper<"buybandwidth"_n, &system_contract::buybandwidth>; + using configcpu_action = eosio::action_wrapper<"configrentbw"_n, &system_contract::configrentbw>; + using rentbw_action = eosio::action_wrapper<"rentbw"_n, &system_contract::rentbw>; private: // Implementation details: @@ -1288,10 +1288,10 @@ namespace eosiosystem { registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; - // defined in buybandwidth.cpp + // defined in rentbw.cpp void adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); - void process_buybw_queue(symbol core_symbol, buybw_state& state, buybw_order_table& orders, uint32_t max_items); - void update_buybw_state(buybw_state& state); + void process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, uint32_t max_items); + void update_rentbw_state(rentbw_state& state); }; } diff --git a/contracts/eosio.system/src/buybandwidth.cpp b/contracts/eosio.system/src/rentbw.cpp similarity index 73% rename from contracts/eosio.system/src/buybandwidth.cpp rename to contracts/eosio.system/src/rentbw.cpp index eae4abb78..3817131bf 100644 --- a/contracts/eosio.system/src/buybandwidth.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -54,8 +54,8 @@ void system_contract::adjust_resources(name payer, name account, symbol core_sym } } // system_contract::adjust_resources -void system_contract::process_buybw_queue(symbol core_symbol, buybw_state& state, buybw_order_table& orders, - uint32_t max_items) { +void system_contract::process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, + uint32_t max_items) { time_point_sec now = eosio::current_time_point(); auto idx = orders.get_index<"byexpires"_n>(); int64_t total_net = 0; @@ -74,7 +74,7 @@ void system_contract::process_buybw_queue(symbol core_symbol, buybw_state& state adjust_resources(get_self(), reserv_account, core_symbol, total_net, total_cpu, true); } -void update_weight(time_point_sec now, buybw_state_resource& res) { +void update_weight(time_point_sec now, rentbw_state_resource& res) { if (now >= res.target_timestamp) res.weight = res.target_weight; else @@ -84,7 +84,7 @@ void update_weight(time_point_sec now, buybw_state_resource& res) { (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); } -void update_utilization(time_point_sec now, buybw_state_resource& res) { +void update_utilization(time_point_sec now, rentbw_state_resource& res) { if (res.utilization >= res.adjusted_utilization) res.adjusted_utilization = res.utilization; else @@ -95,7 +95,7 @@ void update_utilization(time_point_sec now, buybw_state_resource& res) { res.utilization_timestamp = now; } -void system_contract::update_buybw_state(buybw_state& state) { +void system_contract::update_rentbw_state(rentbw_state& state) { time_point_sec now = eosio::current_time_point(); update_weight(now, state.net); update_weight(now, state.cpu); @@ -103,12 +103,12 @@ void system_contract::update_buybw_state(buybw_state& state) { update_utilization(now, state.cpu); } -void system_contract::configbuybw(buybw_config& args) { +void system_contract::configrentbw(rentbw_config& args) { require_auth(get_self()); - time_point_sec now = eosio::current_time_point(); - auto core_symbol = get_core_symbol(); - buybw_state_singleton state_sing{ get_self(), 0 }; - auto state = state_sing.get_or_default(); + time_point_sec now = eosio::current_time_point(); + auto core_symbol = get_core_symbol(); + rentbw_state_singleton state_sing{ get_self(), 0 }; + auto state = state_sing.get_or_default(); auto update = [&](auto& state, auto& args, auto& delta_weight) { if (args.current_weight == args.target_weight) @@ -121,23 +121,23 @@ void system_contract::configbuybw(buybw_config& args) { eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); eosio::check(args.total_price.symbol == core_symbol, "total_price doesn't match core symbol"); eosio::check(args.total_price.amount > 0, "total_price must be positive"); - eosio::check(args.min_purchase_price.symbol == core_symbol, "min_purchase_price doesn't match core symbol"); + eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); delta_weight = args.current_weight - state.weight; - state.weight = args.current_weight; - state.initial_weight = args.current_weight; - state.target_weight = args.target_weight; - state.initial_timestamp = now; - state.target_timestamp = args.target_timestamp; - state.exponent = args.exponent; - state.decay_secs = args.decay_secs; - state.total_price = args.total_price; - state.min_purchase_price = args.min_purchase_price; + state.weight = args.current_weight; + state.initial_weight = args.current_weight; + state.target_weight = args.target_weight; + state.initial_timestamp = now; + state.target_timestamp = args.target_timestamp; + state.exponent = args.exponent; + state.decay_secs = args.decay_secs; + state.total_price = args.total_price; + state.min_rent_price = args.min_rent_price; }; - eosio::check(args.purchase_days > 0, "purchase_days must be > 0"); - state.purchase_days = args.purchase_days; + eosio::check(args.rent_days > 0, "rent_days must be > 0"); + state.rent_days = args.rent_days; int64_t net_delta = 0; int64_t cpu_delta = 0; @@ -146,18 +146,18 @@ void system_contract::configbuybw(buybw_config& args) { adjust_resources(get_self(), reserv_account, core_symbol, net_delta, cpu_delta, true); state_sing.set(state, get_self()); -} // system_contract::configbuybw +} // system_contract::configrentbw -void system_contract::buybandwidth(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, - const asset& max_payment) { +void system_contract::rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, + const asset& max_payment) { require_auth(payer); - buybw_state_singleton state_sing{ get_self(), 0 }; - buybw_order_table orders{ get_self(), 0 }; - eosio::check(state_sing.exists(), "buybandwidth hasn't been initialized"); + rentbw_state_singleton state_sing{ get_self(), 0 }; + rentbw_order_table orders{ get_self(), 0 }; + eosio::check(state_sing.exists(), "rentbw hasn't been initialized"); auto state = state_sing.get(); auto core_symbol = get_core_symbol(); - process_buybw_queue(core_symbol, state, orders, 2); - update_buybw_state(state); + process_rentbw_queue(core_symbol, state, orders, 2); + update_rentbw_state(state); state_sing.set(state, get_self()); } diff --git a/tests/eosio.buybandwidth_tests.cpp b/tests/eosio.rentbw_tests.cpp similarity index 90% rename from tests/eosio.buybandwidth_tests.cpp rename to tests/eosio.rentbw_tests.cpp index 41ecadcb9..d71cdb8c9 100644 --- a/tests/eosio.buybandwidth_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -14,7 +14,7 @@ using namespace eosio_system; -BOOST_AUTO_TEST_SUITE(eosio_system_buybandwidth_tests) +BOOST_AUTO_TEST_SUITE(eosio_system_rentbw_tests) BOOST_FIXTURE_TEST_CASE(foo, eosio_system_tester) try { // From 6862ca12b1243d39c80fba7b90209d836dd8f43f Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 2 Dec 2019 18:45:20 -0500 Subject: [PATCH 08/45] rentbw --- .../include/eosio.system/eosio.system.hpp | 65 +++++++--- contracts/eosio.system/src/rentbw.cpp | 122 +++++++++++------- 2 files changed, 119 insertions(+), 68 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 5fc33bcdc..45f4112f8 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -38,6 +38,8 @@ namespace eosiosystem { using eosio::time_point_sec; using eosio::unsigned_int; + inline constexpr int64_t rentbw_ratio_frac = 1000000000000000ll; // 1.0 = 10^15 + template static inline auto has_field( F flags, E field ) -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && @@ -426,14 +428,28 @@ namespace eosiosystem { }; struct rentbw_config_resource { - int64_t current_weight; // Immediately set the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. - int64_t target_weight; // Linearly grow the resource market weight to this amount. 1 represents the same amount of resources as 1 satoshi of SYS staked. - time_point_sec target_timestamp; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. - // Ignored if current_weight == target_weight. + int64_t current_weight_ratio; // Immediately set weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. Set this + // to 0 to preserve the existing setting or use the default; this avoids sudden + // price jumps. For new chains which don't need to gradually phase out staking + // and REX, 0.01x (10^13) is a good value for both current_weight_ratio and + // target_weight_ratio. + int64_t target_weight_ratio; // Linearly shrink weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. Set this + // to 0 to preserve the existing setting or use the default. + int64_t assumed_stake_weight; // Assumed stake weight for ratio calculations. Use the sum of total staked and + // total rented by REX at the time the rentbw market is first activated. Set + // this to 0 to preserve the existing setting; this avoids sudden price jumps. + // For new chains which don't need to phase out staking and REX, 10^12 is + // probably a good value. + time_point_sec target_timestamp; // Stop automatic weight_ratio shrinkage at this time. Once this + // time hits, weight_ratio will be target_weight_ratio. Ignored if + // current_weight_ratio == target_weight_ratio. Set this to 0 to preserve the + // existing setting. double exponent; // Exponent of resource price curve. Must be >= 1. - uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). + uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous + // utilization within exp(-1). asset total_price; // Total price needed to buy the entire resource market weight. - asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover RAM costs. + asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover + // RAM costs. }; struct rentbw_config { @@ -444,18 +460,28 @@ namespace eosiosystem { struct rentbw_state_resource { uint8_t version = 0; - int64_t weight = 0; // resource market weight - int64_t initial_weight = 0; // Initial resource market weight used for linear growth - int64_t target_weight = 0; // Linearly grow the resource market weight to this amount - time_point_sec initial_timestamp = {}; // When resource market weight growth started - time_point_sec target_timestamp = {}; // Stop automatic resource market weight growth at this time. Once this time hits, the market weight will be target_weight. - double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to decay to instantaneous utilization within exp(-1). - asset total_price = {}; // Total price needed to buy the entire resource market weight. - asset min_rent_price = {}; // Rents below this amount are rejected - int64_t utilization = 0; // Instantaneous resource utilization. This is the current amount sold. - int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It grows instantly but decays exponentially. - time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated + int64_t weight = 0; // resource market weight. calculated; varies over time. + // 1 represents the same amount of resources as 1 + // satoshi of SYS staked. + int64_t weight_ratio = 0; // resource market weight ratio: + // assumed_stake_weight / (assumed_stake_weight + weight). + // calculated; varies over time. 1x = 10^15. 0.01x = 10^13. + int64_t assumed_stake_weight = 0; // Assumed stake weight for ratio calculations. + int64_t initial_weight_ratio = rentbw_ratio_frac; // Initial weight_ratio used for linear shrinkage. + int64_t target_weight_ratio = rentbw_ratio_frac / 100; // Linearly shrink the weight_ratio to this amount. + time_point_sec initial_timestamp = {}; // When weight_ratio shrinkage started + time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this + // time hits, weight_ratio will be target_weight_ratio. + double exponent = 0; // Exponent of resource price curve. + uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to + // decay to instantaneous utilization within exp(-1). + asset total_price = {}; // Total price needed to buy the entire resource market weight. + asset min_rent_price = {}; // Rents below this amount are rejected + int64_t utilization = 0; // Instantaneous resource utilization. This is the current + // amount sold. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It + // grows instantly but decays exponentially. + time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { @@ -1290,8 +1316,7 @@ namespace eosiosystem { // defined in rentbw.cpp void adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); - void process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, uint32_t max_items); - void update_rentbw_state(rentbw_state& state); + void process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, uint32_t max_items, int64_t& net_delta_available, int64_t& cpu_delta_available); }; } diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 3817131bf..5fc0f18d2 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -55,33 +55,36 @@ void system_contract::adjust_resources(name payer, name account, symbol core_sym } // system_contract::adjust_resources void system_contract::process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, - uint32_t max_items) { - time_point_sec now = eosio::current_time_point(); - auto idx = orders.get_index<"byexpires"_n>(); - int64_t total_net = 0; - int64_t total_cpu = 0; + uint32_t max_items, int64_t& net_delta_available, + int64_t& cpu_delta_available) { + time_point_sec now = eosio::current_time_point(); + auto idx = orders.get_index<"byexpires"_n>(); while (max_items--) { auto it = idx.begin(); if (it == idx.end() || it->expires > now) break; - total_net = it->net_weight; - total_cpu = it->cpu_weight; + net_delta_available += it->net_weight; + cpu_delta_available += it->cpu_weight; adjust_resources(get_self(), it->owner, core_symbol, -it->net_weight, -it->cpu_weight); idx.erase(it); } - state.net.utilization -= total_net; - state.cpu.utilization -= total_cpu; - adjust_resources(get_self(), reserv_account, core_symbol, total_net, total_cpu, true); + state.net.utilization -= net_delta_available; + state.cpu.utilization -= cpu_delta_available; } -void update_weight(time_point_sec now, rentbw_state_resource& res) { +void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delta_available) { if (now >= res.target_timestamp) - res.weight = res.target_weight; + res.weight_ratio = res.target_weight_ratio; else - res.weight = res.initial_weight + // - int128_t(res.target_weight - res.initial_weight) * - (now.utc_seconds - res.initial_timestamp.utc_seconds) / - (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); + res.weight_ratio = res.initial_weight_ratio + // + int128_t(res.target_weight_ratio - res.initial_weight_ratio) * + (now.utc_seconds - res.initial_timestamp.utc_seconds) / + (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); + + int64_t new_weight = + res.assumed_stake_weight * int128_t(rentbw_ratio_frac) / res.weight_ratio - res.assumed_stake_weight; + delta_available += new_weight - res.weight; + res.weight = new_weight; } void update_utilization(time_point_sec now, rentbw_state_resource& res) { @@ -95,14 +98,6 @@ void update_utilization(time_point_sec now, rentbw_state_resource& res) { res.utilization_timestamp = now; } -void system_contract::update_rentbw_state(rentbw_state& state) { - time_point_sec now = eosio::current_time_point(); - update_weight(now, state.net); - update_weight(now, state.cpu); - update_utilization(now, state.net); - update_utilization(now, state.cpu); -} - void system_contract::configrentbw(rentbw_config& args) { require_auth(get_self()); time_point_sec now = eosio::current_time_point(); @@ -110,41 +105,59 @@ void system_contract::configrentbw(rentbw_config& args) { rentbw_state_singleton state_sing{ get_self(), 0 }; auto state = state_sing.get_or_default(); - auto update = [&](auto& state, auto& args, auto& delta_weight) { - if (args.current_weight == args.target_weight) + int64_t net_delta_available = 0; + int64_t cpu_delta_available = 0; + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); + + auto update = [&](auto& state, auto& args) { + if (!args.current_weight_ratio) + args.current_weight_ratio = state.weight_ratio; + if (!args.target_weight_ratio) + args.target_weight_ratio = state.target_weight_ratio; + if (!args.assumed_stake_weight) + args.assumed_stake_weight = state.assumed_stake_weight; + if (!args.target_timestamp.utc_seconds) + args.target_timestamp = state.target_timestamp; + + if (args.current_weight_ratio == args.target_weight_ratio) args.target_timestamp = now; else eosio::check(args.target_timestamp > now, "target_timestamp must be in the future"); - eosio::check(args.target_weight >= args.current_weight, "weight can't shrink over time"); - eosio::check(args.current_weight >= state.utilization, "weight can't shrink below utilization"); + eosio::check(args.current_weight_ratio > 0, "current_weight_ratio is too small"); + eosio::check(args.current_weight_ratio <= rentbw_ratio_frac, "current_weight_ratio is too large"); + eosio::check(args.target_weight_ratio <= args.current_weight_ratio, "weight can't grow over time"); + eosio::check(args.assumed_stake_weight >= 1, + "assumed_stake_weight must be at least 1; a much larger value is recommended"); eosio::check(args.exponent >= 1, "exponent must be >= 1"); eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); eosio::check(args.total_price.symbol == core_symbol, "total_price doesn't match core symbol"); eosio::check(args.total_price.amount > 0, "total_price must be positive"); eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); - delta_weight = args.current_weight - state.weight; - - state.weight = args.current_weight; - state.initial_weight = args.current_weight; - state.target_weight = args.target_weight; - state.initial_timestamp = now; - state.target_timestamp = args.target_timestamp; - state.exponent = args.exponent; - state.decay_secs = args.decay_secs; - state.total_price = args.total_price; - state.min_rent_price = args.min_rent_price; + state.assumed_stake_weight = args.assumed_stake_weight; + state.initial_weight_ratio = args.current_weight_ratio; + state.target_weight_ratio = args.target_weight_ratio; + state.initial_timestamp = now; + state.target_timestamp = args.target_timestamp; + state.exponent = args.exponent; + state.decay_secs = args.decay_secs; + state.total_price = args.total_price; + state.min_rent_price = args.min_rent_price; }; eosio::check(args.rent_days > 0, "rent_days must be > 0"); state.rent_days = args.rent_days; - int64_t net_delta = 0; - int64_t cpu_delta = 0; - update(state.net, args.net, net_delta); - update(state.cpu, args.cpu, cpu_delta); + update(state.net, args.net); + update(state.cpu, args.cpu); - adjust_resources(get_self(), reserv_account, core_symbol, net_delta, cpu_delta, true); + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); + eosio::check(state.net.weight >= state.net.utilization, "weight can't shrink below utilization"); + eosio::check(state.cpu.weight >= state.cpu.utilization, "weight can't shrink below utilization"); + + adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); state_sing.set(state, get_self()); } // system_contract::configrentbw @@ -154,10 +167,23 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d rentbw_state_singleton state_sing{ get_self(), 0 }; rentbw_order_table orders{ get_self(), 0 }; eosio::check(state_sing.exists(), "rentbw hasn't been initialized"); - auto state = state_sing.get(); - auto core_symbol = get_core_symbol(); - process_rentbw_queue(core_symbol, state, orders, 2); - update_rentbw_state(state); + auto state = state_sing.get(); + time_point_sec now = eosio::current_time_point(); + auto core_symbol = get_core_symbol(); + eosio::check(max_payment.symbol == core_symbol, "max_payment doesn't match core symbol"); + + int64_t net_delta_available = 0; + int64_t cpu_delta_available = 0; + process_rentbw_queue(core_symbol, state, orders, 2, net_delta_available, cpu_delta_available); + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); + update_utilization(now, state.net); + update_utilization(now, state.cpu); + + // todo: rent + // todo: check against min_rent_price + + adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); state_sing.set(state, get_self()); } From a8ae4e02e2d951098a13050e163eb95dbd97e6a8 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 3 Dec 2019 13:56:38 -0500 Subject: [PATCH 09/45] rentbw --- .../include/eosio.system/eosio.system.hpp | 18 ++++++--- contracts/eosio.system/src/rentbw.cpp | 40 ++++++++++++++----- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 45f4112f8..3a7c42ed4 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -444,18 +444,21 @@ namespace eosiosystem { // time hits, weight_ratio will be target_weight_ratio. Ignored if // current_weight_ratio == target_weight_ratio. Set this to 0 to preserve the // existing setting. - double exponent; // Exponent of resource price curve. Must be >= 1. + double exponent; // Exponent of resource price curve. Must be >= 1. Set this to 0 to preserve the + // existing setting. uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous - // utilization within exp(-1). - asset total_price; // Total price needed to buy the entire resource market weight. + // utilization within exp(-1). Set this to 0 to preserve the existing setting. + asset total_price; // Total price needed to buy the entire resource market weight. Set this to 0 to + // preserve the existing setting. asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover - // RAM costs. + // RAM costs. Set this to 0 to preserve the existing setting. }; struct rentbw_config { rentbw_config_resource net; // NET market configuration rentbw_config_resource cpu; // CPU market configuration - uint32_t rent_days; // `rentbw` `days` argument must match this. + uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the + // existing setting. }; struct rentbw_state_resource { @@ -1316,7 +1319,10 @@ namespace eosiosystem { // defined in rentbw.cpp void adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed = false); - void process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, uint32_t max_items, int64_t& net_delta_available, int64_t& cpu_delta_available); + void process_rentbw_queue( + time_point_sec now, symbol core_symbol, rentbw_state& state, + rentbw_order_table& orders, uint32_t max_items, int64_t& net_delta_available, + int64_t& cpu_delta_available); }; } diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 5fc0f18d2..593c831df 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -54,11 +54,10 @@ void system_contract::adjust_resources(name payer, name account, symbol core_sym } } // system_contract::adjust_resources -void system_contract::process_rentbw_queue(symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, - uint32_t max_items, int64_t& net_delta_available, +void system_contract::process_rentbw_queue(time_point_sec now, symbol core_symbol, rentbw_state& state, + rentbw_order_table& orders, uint32_t max_items, int64_t& net_delta_available, int64_t& cpu_delta_available) { - time_point_sec now = eosio::current_time_point(); - auto idx = orders.get_index<"byexpires"_n>(); + auto idx = orders.get_index<"byexpires"_n>(); while (max_items--) { auto it = idx.begin(); if (it == idx.end() || it->expires > now) @@ -80,7 +79,7 @@ void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delt int128_t(res.target_weight_ratio - res.initial_weight_ratio) * (now.utc_seconds - res.initial_timestamp.utc_seconds) / (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); - + // !!! check bounds of weight_ratio int64_t new_weight = res.assumed_stake_weight * int128_t(rentbw_ratio_frac) / res.weight_ratio - res.assumed_stake_weight; delta_available += new_weight - res.weight; @@ -107,25 +106,41 @@ void system_contract::configrentbw(rentbw_config& args) { int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; - update_weight(now, state.net, net_delta_available); - update_weight(now, state.cpu, cpu_delta_available); + if (state_sing.exists()) { + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); + } auto update = [&](auto& state, auto& args) { - if (!args.current_weight_ratio) - args.current_weight_ratio = state.weight_ratio; + if (!args.current_weight_ratio) { + if (state.weight_ratio) + args.current_weight_ratio = state.weight_ratio; + else + args.current_weight_ratio = state.initial_weight_ratio; + } if (!args.target_weight_ratio) args.target_weight_ratio = state.target_weight_ratio; if (!args.assumed_stake_weight) args.assumed_stake_weight = state.assumed_stake_weight; if (!args.target_timestamp.utc_seconds) args.target_timestamp = state.target_timestamp; - + if (!args.exponent) + args.exponent = state.exponent; + if (!args.decay_secs) + args.decay_secs = state.decay_secs; + if (!args.total_price.amount) + args.total_price = state.total_price; + if (!args.min_rent_price.amount) + args.min_rent_price = state.min_rent_price; + + // !!! examine checks if (args.current_weight_ratio == args.target_weight_ratio) args.target_timestamp = now; else eosio::check(args.target_timestamp > now, "target_timestamp must be in the future"); eosio::check(args.current_weight_ratio > 0, "current_weight_ratio is too small"); eosio::check(args.current_weight_ratio <= rentbw_ratio_frac, "current_weight_ratio is too large"); + eosio::check(args.target_weight_ratio > 0, "target_weight_ratio is too small"); eosio::check(args.target_weight_ratio <= args.current_weight_ratio, "weight can't grow over time"); eosio::check(args.assumed_stake_weight >= 1, "assumed_stake_weight must be at least 1; a much larger value is recommended"); @@ -134,6 +149,7 @@ void system_contract::configrentbw(rentbw_config& args) { eosio::check(args.total_price.symbol == core_symbol, "total_price doesn't match core symbol"); eosio::check(args.total_price.amount > 0, "total_price must be positive"); eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); + eosio::check(args.min_rent_price.amount > 0, "min_rent_price must be positive"); state.assumed_stake_weight = args.assumed_stake_weight; state.initial_weight_ratio = args.current_weight_ratio; @@ -146,6 +162,8 @@ void system_contract::configrentbw(rentbw_config& args) { state.min_rent_price = args.min_rent_price; }; + if (!args.rent_days) + args.rent_days = state.rent_days; eosio::check(args.rent_days > 0, "rent_days must be > 0"); state.rent_days = args.rent_days; @@ -174,7 +192,7 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; - process_rentbw_queue(core_symbol, state, orders, 2, net_delta_available, cpu_delta_available); + process_rentbw_queue(now, core_symbol, state, orders, 2, net_delta_available, cpu_delta_available); update_weight(now, state.net, net_delta_available); update_weight(now, state.cpu, cpu_delta_available); update_utilization(now, state.net); From 95d073453a4bd9faf8095456ca7f730492b006fe Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 3 Dec 2019 18:13:12 -0500 Subject: [PATCH 10/45] rentbw --- .../include/eosio.system/eosio.system.hpp | 70 +++++++++---------- contracts/eosio.system/src/rentbw.cpp | 69 +++++++++++++----- contracts/eosio.system/src/rex.cpp | 4 +- 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 3a7c42ed4..f8fdc9974 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -38,8 +38,8 @@ namespace eosiosystem { using eosio::time_point_sec; using eosio::unsigned_int; - inline constexpr int64_t rentbw_ratio_frac = 1000000000000000ll; // 1.0 = 10^15 - + inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 + template static inline auto has_field( F flags, E field ) -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && @@ -448,43 +448,42 @@ namespace eosiosystem { // existing setting. uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous // utilization within exp(-1). Set this to 0 to preserve the existing setting. - asset total_price; // Total price needed to buy the entire resource market weight. Set this to 0 to + asset target_price; // Fee needed to rent the entire resource market weight. Set this to 0 to // preserve the existing setting. - asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover - // RAM costs. Set this to 0 to preserve the existing setting. }; struct rentbw_config { - rentbw_config_resource net; // NET market configuration - rentbw_config_resource cpu; // CPU market configuration - uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the - // existing setting. + rentbw_config_resource net; // NET market configuration + rentbw_config_resource cpu; // CPU market configuration + uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the + // existing setting. + asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover + // RAM costs. Set this to 0 to preserve the existing setting. }; struct rentbw_state_resource { uint8_t version = 0; - int64_t weight = 0; // resource market weight. calculated; varies over time. - // 1 represents the same amount of resources as 1 - // satoshi of SYS staked. - int64_t weight_ratio = 0; // resource market weight ratio: - // assumed_stake_weight / (assumed_stake_weight + weight). - // calculated; varies over time. 1x = 10^15. 0.01x = 10^13. - int64_t assumed_stake_weight = 0; // Assumed stake weight for ratio calculations. - int64_t initial_weight_ratio = rentbw_ratio_frac; // Initial weight_ratio used for linear shrinkage. - int64_t target_weight_ratio = rentbw_ratio_frac / 100; // Linearly shrink the weight_ratio to this amount. - time_point_sec initial_timestamp = {}; // When weight_ratio shrinkage started - time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this - // time hits, weight_ratio will be target_weight_ratio. - double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to - // decay to instantaneous utilization within exp(-1). - asset total_price = {}; // Total price needed to buy the entire resource market weight. - asset min_rent_price = {}; // Rents below this amount are rejected - int64_t utilization = 0; // Instantaneous resource utilization. This is the current - // amount sold. - int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It - // grows instantly but decays exponentially. - time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated + int64_t weight = 0; // resource market weight. calculated; varies over time. + // 1 represents the same amount of resources as 1 + // satoshi of SYS staked. + int64_t weight_ratio = 0; // resource market weight ratio: + // assumed_stake_weight / (assumed_stake_weight + weight). + // calculated; varies over time. 1x = 10^15. 0.01x = 10^13. + int64_t assumed_stake_weight = 0; // Assumed stake weight for ratio calculations. + int64_t initial_weight_ratio = rentbw_frac; // Initial weight_ratio used for linear shrinkage. + int64_t target_weight_ratio = rentbw_frac / 100; // Linearly shrink the weight_ratio to this amount. + time_point_sec initial_timestamp = {}; // When weight_ratio shrinkage started + time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this + // time hits, weight_ratio will be target_weight_ratio. + double exponent = 0; // Exponent of resource price curve. + uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to + // decay to instantaneous utilization within exp(-1). + asset target_price = {}; // Fee needed to rent the entire resource market weight. + int64_t utilization = 0; // Instantaneous resource utilization. This is the current + // amount sold. utilization <= weight. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It + // grows instantly but decays exponentially. + time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { @@ -492,6 +491,7 @@ namespace eosiosystem { rentbw_state_resource net = {}; // NET market state rentbw_state_resource cpu = {}; // CPU market state uint32_t rent_days = 0; // `rentbw` `days` argument must match this. + asset min_rent_price = {}; // Rents below this amount are rejected uint64_t primary_key()const { return 0; } }; @@ -1164,13 +1164,13 @@ namespace eosiosystem { * @param payer - the resource buyer * @param receiver - the resource receiver * @param days - number of days of resource availability. Must match market configuration. - * @param net - fraction of net (100% = 10^18) managed by this market - * @param cpu - fraction of cpu (100% = 10^18) managed by this market + * @param net_frac - fraction of net (100% = 10^15) managed by this market + * @param cpu_frac - fraction of cpu (100% = 10^15) managed by this market * @param max_payment - the maximum amount `payer` is willing to pay. Tokens are withdrawn from * `payer`'s token balance. */ [[eosio::action]] - void rentbw( const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, const asset& max_payment ); + void rentbw( const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, const asset& max_payment ); using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; @@ -1242,7 +1242,7 @@ namespace eosiosystem { const char* error_msg = "must vote for at least 21 producers or for a proxy before buying REX" )const; rex_order_outcome fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex ); asset update_rex_account( const name& owner, const asset& proceeds, const asset& unstake_quant, bool force_vote_update = false ); - void channel_to_rex( const name& from, const asset& amount ); + void channel_to_rex( const name& from, const asset& amount, bool required = false ); void channel_namebid_to_rex( const int64_t highest_bid ); template int64_t rent_rex( T& table, const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 593c831df..cca3173ef 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -80,8 +80,7 @@ void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delt (now.utc_seconds - res.initial_timestamp.utc_seconds) / (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); // !!! check bounds of weight_ratio - int64_t new_weight = - res.assumed_stake_weight * int128_t(rentbw_ratio_frac) / res.weight_ratio - res.assumed_stake_weight; + int64_t new_weight = res.assumed_stake_weight * int128_t(rentbw_frac) / res.weight_ratio - res.assumed_stake_weight; delta_available += new_weight - res.weight; res.weight = new_weight; } @@ -128,10 +127,8 @@ void system_contract::configrentbw(rentbw_config& args) { args.exponent = state.exponent; if (!args.decay_secs) args.decay_secs = state.decay_secs; - if (!args.total_price.amount) - args.total_price = state.total_price; - if (!args.min_rent_price.amount) - args.min_rent_price = state.min_rent_price; + if (!args.target_price.amount) + args.target_price = state.target_price; // !!! examine checks if (args.current_weight_ratio == args.target_weight_ratio) @@ -139,17 +136,15 @@ void system_contract::configrentbw(rentbw_config& args) { else eosio::check(args.target_timestamp > now, "target_timestamp must be in the future"); eosio::check(args.current_weight_ratio > 0, "current_weight_ratio is too small"); - eosio::check(args.current_weight_ratio <= rentbw_ratio_frac, "current_weight_ratio is too large"); + eosio::check(args.current_weight_ratio <= rentbw_frac, "current_weight_ratio is too large"); eosio::check(args.target_weight_ratio > 0, "target_weight_ratio is too small"); eosio::check(args.target_weight_ratio <= args.current_weight_ratio, "weight can't grow over time"); eosio::check(args.assumed_stake_weight >= 1, "assumed_stake_weight must be at least 1; a much larger value is recommended"); eosio::check(args.exponent >= 1, "exponent must be >= 1"); eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); - eosio::check(args.total_price.symbol == core_symbol, "total_price doesn't match core symbol"); - eosio::check(args.total_price.amount > 0, "total_price must be positive"); - eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); - eosio::check(args.min_rent_price.amount > 0, "min_rent_price must be positive"); + eosio::check(args.target_price.symbol == core_symbol, "target_price doesn't match core symbol"); + eosio::check(args.target_price.amount > 0, "target_price must be positive"); state.assumed_stake_weight = args.assumed_stake_weight; state.initial_weight_ratio = args.current_weight_ratio; @@ -158,14 +153,20 @@ void system_contract::configrentbw(rentbw_config& args) { state.target_timestamp = args.target_timestamp; state.exponent = args.exponent; state.decay_secs = args.decay_secs; - state.total_price = args.total_price; - state.min_rent_price = args.min_rent_price; + state.target_price = args.target_price; }; if (!args.rent_days) args.rent_days = state.rent_days; + if (!args.min_rent_price.amount) + args.min_rent_price = state.min_rent_price; + eosio::check(args.rent_days > 0, "rent_days must be > 0"); - state.rent_days = args.rent_days; + eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); + eosio::check(args.min_rent_price.amount > 0, "min_rent_price must be positive"); + + state.rent_days = args.rent_days; + state.min_rent_price = args.min_rent_price; update(state.net, args.net); update(state.cpu, args.cpu); @@ -179,7 +180,11 @@ void system_contract::configrentbw(rentbw_config& args) { state_sing.set(state, get_self()); } // system_contract::configrentbw -void system_contract::rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net, int64_t cpu, +int64_t calc_rentbw_price(const rentbw_state_resource& state, double utilization) { + return ceil(state.target_price.amount * pow(utilization / state.weight, state.exponent)); +} + +void system_contract::rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, const asset& max_payment) { require_auth(payer); rentbw_state_singleton state_sing{ get_self(), 0 }; @@ -189,6 +194,11 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d time_point_sec now = eosio::current_time_point(); auto core_symbol = get_core_symbol(); eosio::check(max_payment.symbol == core_symbol, "max_payment doesn't match core symbol"); + eosio::check(days == state.rent_days, "days doesn't match configuration"); + eosio::check(net_frac >= 0, "net_frac can't be negative"); + eosio::check(cpu_frac >= 0, "cpu_frac can't be negative"); + eosio::check(net_frac <= rentbw_frac, "net can't be more than 100%"); + eosio::check(cpu_frac <= rentbw_frac, "cpu can't be more than 100%"); int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; @@ -198,10 +208,35 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d update_utilization(now, state.net); update_utilization(now, state.cpu); - // todo: rent - // todo: check against min_rent_price + eosio::asset fee{ 0, core_symbol }; + auto process = [&](int64_t frac, int64_t& amount, rentbw_state_resource& state) { + if (!frac) + return; + amount = int128_t(frac) * state.weight / rentbw_frac; + fee += calc_rentbw_price(state, state.adjusted_utilization + amount) - + calc_rentbw_price(state, state.adjusted_utilization); + state.utilization += amount; + eosio::check(state.utilization <= state.weight, "market doesn't have enough resources available"); + }; + int64_t net_amount = 0; + int64_t cpu_amount = 0; + process(net_frac, net_amount, state.net); + process(cpu_frac, cpu_amount, state.cpu); + eosio::check(fee <= max_payment, "calculated fee exceeds max_payment"); + eosio::check(fee >= state.min_rent_price, "calculated fee is below minimum; try renting more"); + + orders.emplace([&](payer, auto& order) { + order.id = orders.available_primary_key(); + order.owner = receiver; + order.net_weight = net_amount; + order.cpu_weight = cpu_amount; + order.expires = now + eosio::days(days); + }); + + adjust_resources(payer, receiver, core_symbol, net, cpu, true); adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); + channel_to_rex(payer, fee, true); state_sing.set(state, get_self()); } diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index 4a4596e25..dc09be6de 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -814,7 +814,7 @@ namespace eosiosystem { * @param from - account from which asset is transfered to REX pool * @param amount - amount of tokens to be transfered */ - void system_contract::channel_to_rex( const name& from, const asset& amount ) + void system_contract::channel_to_rex( const name& from, const asset& amount, bool required ) { #if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX if ( rex_available() ) { @@ -826,8 +826,10 @@ namespace eosiosystem { token::transfer_action transfer_act{ token_account, { from, active_permission } }; transfer_act.send( from, rex_account, amount, std::string("transfer from ") + from.to_string() + " to eosio.rex" ); + return; } #endif + eosio::check( !required, "can't channel fees to rex" ); } /** From aba9d54db0d4b7cacc75c94b4026cf41ab3c7387 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 4 Dec 2019 13:59:49 -0500 Subject: [PATCH 11/45] testing --- .../include/eosio.system/eosio.system.hpp | 2 +- contracts/eosio.system/src/rentbw.cpp | 12 +- tests/eosio.rentbw_tests.cpp | 126 +++++++++++++++++- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index f8fdc9974..a1b3a205c 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -39,7 +39,7 @@ namespace eosiosystem { using eosio::unsigned_int; inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 - + template static inline auto has_field( F flags, E field ) -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index cca3173ef..cad25115e 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -127,7 +127,7 @@ void system_contract::configrentbw(rentbw_config& args) { args.exponent = state.exponent; if (!args.decay_secs) args.decay_secs = state.decay_secs; - if (!args.target_price.amount) + if (!args.target_price.amount && state.target_price.amount) args.target_price = state.target_price; // !!! examine checks @@ -158,7 +158,7 @@ void system_contract::configrentbw(rentbw_config& args) { if (!args.rent_days) args.rent_days = state.rent_days; - if (!args.min_rent_price.amount) + if (!args.min_rent_price.amount && state.min_rent_price.amount) args.min_rent_price = state.min_rent_price; eosio::check(args.rent_days > 0, "rent_days must be > 0"); @@ -213,8 +213,8 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d if (!frac) return; amount = int128_t(frac) * state.weight / rentbw_frac; - fee += calc_rentbw_price(state, state.adjusted_utilization + amount) - - calc_rentbw_price(state, state.adjusted_utilization); + fee.amount += calc_rentbw_price(state, state.adjusted_utilization + amount) - + calc_rentbw_price(state, state.adjusted_utilization); state.utilization += amount; eosio::check(state.utilization <= state.weight, "market doesn't have enough resources available"); }; @@ -226,7 +226,7 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d eosio::check(fee <= max_payment, "calculated fee exceeds max_payment"); eosio::check(fee >= state.min_rent_price, "calculated fee is below minimum; try renting more"); - orders.emplace([&](payer, auto& order) { + orders.emplace(payer, [&](auto& order) { order.id = orders.available_primary_key(); order.owner = receiver; order.net_weight = net_amount; @@ -234,7 +234,7 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d order.expires = now + eosio::days(days); }); - adjust_resources(payer, receiver, core_symbol, net, cpu, true); + adjust_resources(payer, receiver, core_symbol, net_amount, cpu_amount, true); adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); channel_to_rex(payer, fee, true); state_sing.set(state, get_self()); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index d71cdb8c9..b3c38a9dc 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -12,13 +12,135 @@ #include "eosio.system_tester.hpp" +inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 +inline constexpr int64_t stake_weight = 1000000000000ll; // 10^12 + +struct rentbw_config_resource { + int64_t current_weight_ratio = {}; + int64_t target_weight_ratio = {}; + int64_t assumed_stake_weight = {}; + time_point_sec target_timestamp = {}; + double exponent = {}; + uint32_t decay_secs = {}; + asset target_price = asset{}; +}; +FC_REFLECT(rentbw_config_resource, // + (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight)(target_timestamp) // + (exponent)(decay_secs)(target_price)) + +struct rentbw_config { + rentbw_config_resource net = {}; + rentbw_config_resource cpu = {}; + uint32_t rent_days = {}; + asset min_rent_price = asset{}; +}; +FC_REFLECT(rentbw_config, (net)(cpu)(rent_days)(min_rent_price)) + using namespace eosio_system; +struct rentbw_tester : eosio_system_tester { + + template + rentbw_config make_config(F f) { + rentbw_config config; + + config.net.current_weight_ratio = rentbw_frac; + config.net.target_weight_ratio = rentbw_frac / 100; + config.net.assumed_stake_weight = stake_weight; + config.net.target_timestamp = control->head_block_time() + fc::days(100); + config.net.exponent = 2; + config.net.decay_secs = fc::days(1).to_seconds(); + config.net.target_price = asset::from_string("1000000.0000 TST"); + + config.cpu.current_weight_ratio = rentbw_frac; + config.cpu.target_weight_ratio = rentbw_frac / 100; + config.cpu.assumed_stake_weight = stake_weight; + config.cpu.target_timestamp = control->head_block_time() + fc::days(100); + config.cpu.exponent = 2; + config.cpu.decay_secs = fc::days(1).to_seconds(); + config.cpu.target_price = asset::from_string("1000000.0000 TST"); + + config.rent_days = 30; + config.min_rent_price = asset::from_string("1.0000 TST"); + + f(config); + return config; + } + + rentbw_config make_config() { + return make_config([](auto&) {}); + } + + action_result configbw(const rentbw_config& config) { + return push_action(N(eosio), N(configrentbw), mvo()("args", config)); + } +}; + BOOST_AUTO_TEST_SUITE(eosio_system_rentbw_tests) -BOOST_FIXTURE_TEST_CASE(foo, eosio_system_tester) try { - // +BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { + BOOST_REQUIRE_EQUAL("missing authority of eosio", + push_action(N(alice1111111), N(configrentbw), mvo()("args", make_config()))); + + BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), + configbw(make_config([&](auto& c) { c.rent_days = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.min_rent_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), + configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), + configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("-1.0000 TST"); }))); + + // net assertions + BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), + configbw(make_config([](auto& c) { c.net.current_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), + configbw(make_config([](auto& c) { c.net.target_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), + configbw(make_config([](auto& c) { c.net.assumed_stake_weight = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), + configbw(make_config([&](auto& c) { c.net.target_timestamp = control->head_block_time(); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { + c.net.target_timestamp = control->head_block_time() - fc::seconds(1); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), + configbw(make_config([&](auto& c) { c.net.exponent = .999; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.net.target_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("-1.0000 TST"); }))); + + // cpu assertions + BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), + configbw(make_config([](auto& c) { c.cpu.current_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), + configbw(make_config([](auto& c) { c.cpu.target_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), + configbw(make_config([](auto& c) { c.cpu.assumed_stake_weight = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), + configbw(make_config([&](auto& c) { c.cpu.target_timestamp = control->head_block_time(); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { + c.cpu.target_timestamp = control->head_block_time() - fc::seconds(1); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), + configbw(make_config([&](auto& c) { c.cpu.exponent = .999; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.cpu.target_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); } + FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() From 1933e431d17413c31a3c2447835a97b162cdb2eb Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 4 Dec 2019 18:54:29 -0500 Subject: [PATCH 12/45] testing --- .../include/eosio.system/eosio.system.hpp | 11 ++- contracts/eosio.system/src/rentbw.cpp | 21 ++++++ tests/eosio.rentbw_tests.cpp | 69 ++++++++++++++++++- tests/main.cpp | 7 +- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index a1b3a205c..665e5de46 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -1156,7 +1156,16 @@ namespace eosiosystem { * action is invoked. */ [[eosio::action]] - void configrentbw(rentbw_config& args); + void configrentbw( rentbw_config& args ); + + /** + * Process rentbw queue and update state. Action does not execute anything related to a specific user. + * + * @param user - any account can execute this action + * @param max - number of queue items to process + */ + [[eosio::action]] + void rentbwexec( const name& user, uint16_t max ); /** * Rent NET and CPU diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index cad25115e..2d5d43eda 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -184,6 +184,27 @@ int64_t calc_rentbw_price(const rentbw_state_resource& state, double utilization return ceil(state.target_price.amount * pow(utilization / state.weight, state.exponent)); } +void system_contract::rentbwexec(const name& user, uint16_t max) { + require_auth(user); + rentbw_state_singleton state_sing{ get_self(), 0 }; + rentbw_order_table orders{ get_self(), 0 }; + eosio::check(state_sing.exists(), "rentbw hasn't been initialized"); + auto state = state_sing.get(); + time_point_sec now = eosio::current_time_point(); + auto core_symbol = get_core_symbol(); + + int64_t net_delta_available = 0; + int64_t cpu_delta_available = 0; + process_rentbw_queue(now, core_symbol, state, orders, max, net_delta_available, cpu_delta_available); + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); + update_utilization(now, state.net); + update_utilization(now, state.cpu); + + adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); + state_sing.set(state, get_self()); +} + void system_contract::rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, const asset& max_payment) { require_auth(payer); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index b3c38a9dc..605150681 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -36,10 +36,42 @@ struct rentbw_config { }; FC_REFLECT(rentbw_config, (net)(cpu)(rent_days)(min_rent_price)) +struct rentbw_state_resource { + uint8_t version; + int64_t weight; + int64_t weight_ratio; + int64_t assumed_stake_weight; + int64_t initial_weight_ratio; + int64_t target_weight_ratio; + time_point_sec initial_timestamp; + time_point_sec target_timestamp; + double exponent; + uint32_t decay_secs; + asset target_price; + int64_t utilization; + int64_t adjusted_utilization; + time_point_sec utilization_timestamp; +}; +FC_REFLECT(rentbw_state_resource, // + (version)(weight)(weight_ratio)(assumed_stake_weight)(initial_weight_ratio)(target_weight_ratio) // + (initial_timestamp)(target_timestamp)(exponent)(decay_secs)(target_price)(utilization) // + (adjusted_utilization)(utilization_timestamp)) + +struct rentbw_state { + uint8_t version; + rentbw_state_resource net; + rentbw_state_resource cpu; + uint32_t rent_days; + asset min_rent_price; +}; +FC_REFLECT(rentbw_state, (version)(net)(cpu)(rent_days)(min_rent_price)) + using namespace eosio_system; struct rentbw_tester : eosio_system_tester { + rentbw_tester() { create_accounts_with_resources({ N(eosio.reserv) }); } + template rentbw_config make_config(F f) { rentbw_config config; @@ -72,7 +104,16 @@ struct rentbw_tester : eosio_system_tester { } action_result configbw(const rentbw_config& config) { - return push_action(N(eosio), N(configrentbw), mvo()("args", config)); + return push_action(config::system_account_name, N(configrentbw), mvo()("args", config)); + } + + action_result rentbwexec(name user, uint16_t max) { + return push_action(user, N(rentbwexec), mvo()("user", user)("max", max)); + } + + rentbw_state get_state() { + vector data = get_row_by_account(config::system_account_name, {}, N(rent.state), N(rent.state)); + return fc::raw::unpack(data); } }; @@ -81,6 +122,7 @@ BOOST_AUTO_TEST_SUITE(eosio_system_rentbw_tests) BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL("missing authority of eosio", push_action(N(alice1111111), N(configrentbw), mvo()("args", make_config()))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("rentbw hasn't been initialized"), rentbwexec(N(alice1111111), 10)); BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), configbw(make_config([&](auto& c) { c.rent_days = 0; }))); @@ -139,8 +181,33 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); + + // TODO: "weight can't shrink below utilization" } +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { + produce_block(); + + BOOST_REQUIRE_EQUAL("", configbw(make_config([&](rentbw_config& config) { + config.net.current_weight_ratio = (rentbw_frac * 11) / 100; + config.net.target_weight_ratio = (rentbw_frac * 1) / 100; + config.net.assumed_stake_weight = stake_weight; + config.net.target_timestamp = control->head_block_time() + fc::days(10); + config.cpu.current_weight_ratio = (rentbw_frac * 11) / 1000; + config.cpu.target_weight_ratio = (rentbw_frac * 1) / 1000; + config.cpu.assumed_stake_weight = stake_weight; + config.cpu.target_timestamp = control->head_block_time() + fc::days(10); + }))); + + for (int i = 11; i >= 1; --i) { + BOOST_REQUIRE_EQUAL(get_state().net.weight_ratio, (rentbw_frac * i) / 100); + BOOST_REQUIRE_EQUAL(get_state().cpu.weight_ratio, (rentbw_frac * i) / 1000); + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); + } +} FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/main.cpp b/tests/main.cpp index 2c658f31a..e3d617509 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -31,7 +31,12 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { break; } } - if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); + + if(is_verbose) { + fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); + } else { + fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); + } // Register fc::exception translator boost::unit_test::unit_test_monitor.template register_exception_translator(&translate_fc_exception); From 7c69784c492d6e2ce1a5ad6be5b8b5a965e94233 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 5 Dec 2019 15:50:41 -0500 Subject: [PATCH 13/45] tests --- tests/eosio.rentbw_tests.cpp | 126 ++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 10 deletions(-) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 605150681..518c56c75 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -103,6 +103,13 @@ struct rentbw_tester : eosio_system_tester { return make_config([](auto&) {}); } + template + rentbw_config make_default_config(F f) { + rentbw_config config; + f(config); + return config; + } + action_result configbw(const rentbw_config& config) { return push_action(config::system_account_name, N(configrentbw), mvo()("args", config)); } @@ -117,6 +124,14 @@ struct rentbw_tester : eosio_system_tester { } }; +template +bool near(A a, B b, D delta) { + if (abs(a - b) <= delta) + return true; + elog("near: ${a} ${b}", ("a", a)("b", b)); + return false; +} + BOOST_AUTO_TEST_SUITE(eosio_system_rentbw_tests) BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { @@ -183,31 +198,122 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); // TODO: "weight can't shrink below utilization" -} +} // config_tests FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { produce_block(); + auto net_start = (rentbw_frac * 11) / 100; + auto net_target = (rentbw_frac * 1) / 100; + auto cpu_start = (rentbw_frac * 11) / 1000; + auto cpu_target = (rentbw_frac * 1) / 1000; + BOOST_REQUIRE_EQUAL("", configbw(make_config([&](rentbw_config& config) { - config.net.current_weight_ratio = (rentbw_frac * 11) / 100; - config.net.target_weight_ratio = (rentbw_frac * 1) / 100; + config.net.current_weight_ratio = net_start; + config.net.target_weight_ratio = net_target; config.net.assumed_stake_weight = stake_weight; config.net.target_timestamp = control->head_block_time() + fc::days(10); - config.cpu.current_weight_ratio = (rentbw_frac * 11) / 1000; - config.cpu.target_weight_ratio = (rentbw_frac * 1) / 1000; + config.cpu.current_weight_ratio = cpu_start; + config.cpu.target_weight_ratio = cpu_target; config.cpu.assumed_stake_weight = stake_weight; - config.cpu.target_timestamp = control->head_block_time() + fc::days(10); + config.cpu.target_timestamp = control->head_block_time() + fc::days(20); }))); - for (int i = 11; i >= 1; --i) { - BOOST_REQUIRE_EQUAL(get_state().net.weight_ratio, (rentbw_frac * i) / 100); - BOOST_REQUIRE_EQUAL(get_state().cpu.weight_ratio, (rentbw_frac * i) / 1000); + int64_t net; + int64_t cpu; + + for (int i = 0; i <= 6; ++i) { + if (i == 2) { + // Leaves everything as-is, but may introduce slight rounding + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", configbw({})); + } else if (i) { + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); + } + net = net_start + i * (net_target - net_start) / 10; + cpu = cpu_start + i * (cpu_target - cpu_start) / 20; + BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + } + + // Extend transition time + { + int i = 7; + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", configbw(make_default_config([&](rentbw_config& config) { + config.net.target_timestamp = control->head_block_time() + fc::days(30); + config.cpu.target_timestamp = control->head_block_time() + fc::days(40); + }))); + net_start = net = net_start + i * (net_target - net_start) / 10; + cpu_start = cpu = cpu_start + i * (cpu_target - cpu_start) / 20; + BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + } + + for (int i = 0; i <= 5; ++i) { + if (i) { + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); + } + net = net_start + i * (net_target - net_start) / 30; + cpu = cpu_start + i * (cpu_target - cpu_start) / 40; + BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + } + + // Change target, keep existing transition time + { + int i = 6; + produce_block(fc::days(1) - fc::milliseconds(500)); + auto new_net_target = net_target / 10; + auto new_cpu_target = cpu_target / 20; + BOOST_REQUIRE_EQUAL("", configbw(make_default_config([&](rentbw_config& config) { + config.net.target_weight_ratio = new_net_target; + config.cpu.target_weight_ratio = new_cpu_target; + }))); + net_start = net = net_start + i * (net_target - net_start) / 30; + cpu_start = cpu = cpu_start + i * (cpu_target - cpu_start) / 40; + net_target = new_net_target; + cpu_target = new_cpu_target; + BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + } + + for (int i = 0; i <= 10; ++i) { + if (i) { + produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); + } + net = net_start + i * (net_target - net_start) / (30 - 6); + cpu = cpu_start + i * (cpu_target - cpu_start) / (40 - 6); + BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + } + + // Move transition time to immediate future + { produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", configbw(make_default_config([&](rentbw_config& config) { + config.net.target_timestamp = control->head_block_time() + fc::milliseconds(1000); + config.cpu.target_timestamp = control->head_block_time() + fc::milliseconds(1000); + }))); + produce_blocks(2); + } + + // Verify targets hold as time advances + for (int i = 0; i <= 10; ++i) { BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(get_state().net.weight_ratio, net_target, 1)); + BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu_target, 1)); + produce_block(fc::days(1)); } -} + + // todo: verify calculated weight + +} // weight_tests FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() From 02a4c350e775e5436a98c61f116bce730738ed0f Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 5 Dec 2019 16:14:02 -0500 Subject: [PATCH 14/45] tests --- tests/eosio.rentbw_tests.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 518c56c75..f1bd0c6c9 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -224,9 +224,18 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { int64_t net; int64_t cpu; + auto check_weight = [&] { + auto state = get_state(); + BOOST_REQUIRE(near( // + state.net.weight_ratio, // + int64_t(state.net.assumed_stake_weight * eosio::chain::int128_t(rentbw_frac) / + (state.net.weight + state.net.assumed_stake_weight)), + 10)); + }; + for (int i = 0; i <= 6; ++i) { if (i == 2) { - // Leaves everything as-is, but may introduce slight rounding + // Leaves config as-is, but may introduce slight rounding produce_block(fc::days(1) - fc::milliseconds(500)); BOOST_REQUIRE_EQUAL("", configbw({})); } else if (i) { @@ -237,6 +246,7 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { cpu = cpu_start + i * (cpu_target - cpu_start) / 20; BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + check_weight(); } // Extend transition time @@ -251,6 +261,7 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { cpu_start = cpu = cpu_start + i * (cpu_target - cpu_start) / 20; BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + check_weight(); } for (int i = 0; i <= 5; ++i) { @@ -262,6 +273,7 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { cpu = cpu_start + i * (cpu_target - cpu_start) / 40; BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + check_weight(); } // Change target, keep existing transition time @@ -280,6 +292,7 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { cpu_target = new_cpu_target; BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + check_weight(); } for (int i = 0; i <= 10; ++i) { @@ -291,6 +304,7 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { cpu = cpu_start + i * (cpu_target - cpu_start) / (40 - 6); BOOST_REQUIRE(near(get_state().net.weight_ratio, net, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu, 1)); + check_weight(); } // Move transition time to immediate future @@ -308,11 +322,9 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL("", rentbwexec(config::system_account_name, 10)); BOOST_REQUIRE(near(get_state().net.weight_ratio, net_target, 1)); BOOST_REQUIRE(near(get_state().cpu.weight_ratio, cpu_target, 1)); + check_weight(); produce_block(fc::days(1)); } - - // todo: verify calculated weight - } // weight_tests FC_LOG_AND_RETHROW() From 8b3e6d7bedc514f7449fb99bce87f51754ac30ba Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 6 Dec 2019 14:37:12 -0500 Subject: [PATCH 15/45] tests --- contracts/eosio.system/src/rentbw.cpp | 9 +- tests/eosio.rentbw_tests.cpp | 136 ++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 2d5d43eda..73d5da5b0 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -234,10 +234,13 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d if (!frac) return; amount = int128_t(frac) * state.weight / rentbw_frac; - fee.amount += calc_rentbw_price(state, state.adjusted_utilization + amount) - - calc_rentbw_price(state, state.adjusted_utilization); + eosio::check(state.weight, "market doesn't have resources available"); + eosio::check(state.utilization + amount <= state.weight, "market doesn't have enough resources available"); + int64_t f = calc_rentbw_price(state, state.adjusted_utilization + amount) - + calc_rentbw_price(state, state.adjusted_utilization); + eosio::check(f > 0, "calculated fee is below minimum; try renting more"); + fee.amount += f; state.utilization += amount; - eosio::check(state.utilization <= state.weight, "market doesn't have enough resources available"); }; int64_t net_amount = 0; diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index f1bd0c6c9..ee9fa7208 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -72,6 +72,23 @@ struct rentbw_tester : eosio_system_tester { rentbw_tester() { create_accounts_with_resources({ N(eosio.reserv) }); } + void start_rex() { + create_account_with_resources(N(rexholder111), config::system_account_name, core_sym::from_string("1.0000"), + false); + transfer(config::system_account_name, N(rexholder111), core_sym::from_string("1001.0000")); + BOOST_REQUIRE_EQUAL("", stake(N(rexholder111), N(rexholder111), core_sym::from_string("500.0000"), + core_sym::from_string("500.0000"))); + create_account_with_resources(N(proxyaccount), config::system_account_name, core_sym::from_string("1.0000"), + false, core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + BOOST_REQUIRE_EQUAL("", + push_action(N(proxyaccount), N(regproxy), mvo()("proxy", "proxyaccount")("isproxy", true))); + BOOST_REQUIRE_EQUAL("", vote(N(rexholder111), {}, N(proxyaccount))); + BOOST_REQUIRE_EQUAL("", push_action(N(rexholder111), N(deposit), + mvo()("owner", "rexholder111")("amount", asset::from_string("1.0000 TST")))); + BOOST_REQUIRE_EQUAL("", push_action(N(rexholder111), N(buyrex), + mvo()("from", "rexholder111")("amount", asset::from_string("1.0000 TST")))); + } + template rentbw_config make_config(F f) { rentbw_config config; @@ -118,10 +135,53 @@ struct rentbw_tester : eosio_system_tester { return push_action(user, N(rentbwexec), mvo()("user", user)("max", max)); } + action_result rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, + const asset& max_payment) { + return push_action(payer, N(rentbw), + mvo()("payer", payer)("receiver", receiver)("days", days)("net_frac", net_frac)( + "cpu_frac", cpu_frac)("max_payment", max_payment)); + } + rentbw_state get_state() { vector data = get_row_by_account(config::system_account_name, {}, N(rent.state), N(rent.state)); return fc::raw::unpack(data); } + + struct account_info { + int64_t ram = 0; + int64_t net = 0; + int64_t cpu = 0; + asset liquid; + }; + + account_info get_account_info(account_name acc) { + account_info info; + control->get_resource_limits_manager().get_account_limits(acc, info.ram, info.net, info.cpu); + info.liquid = get_balance(acc); + return info; + }; + + void check_rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, + const asset& max_payment) { + auto before_payer = get_account_info(payer); + auto before_receiver = get_account_info(receiver); + auto before_state = get_state(); + BOOST_REQUIRE_EQUAL("", rentbw(payer, receiver, days, net_frac, cpu_frac, max_payment)); + auto after_payer = get_account_info(payer); + auto after_receiver = get_account_info(receiver); + auto after_state = get_state(); + + if (payer != receiver) { + BOOST_REQUIRE(before_payer.ram == after_payer.ram); + BOOST_REQUIRE(before_payer.net == after_payer.net); + BOOST_REQUIRE(before_payer.cpu == after_payer.cpu); + BOOST_REQUIRE(before_receiver.liquid == after_receiver.liquid); + } + BOOST_REQUIRE(before_receiver.ram == after_receiver.ram); + // BOOST_REQUIRE(before_receiver.net == after_receiver.net); + // BOOST_REQUIRE(before_receiver.cpu == after_receiver.cpu); + // BOOST_REQUIRE(before_payer.liquid == after_payer.liquid); + } }; template @@ -328,4 +388,80 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { } // weight_tests FC_LOG_AND_RETHROW() +BOOST_AUTO_TEST_CASE(rent_tests) try { + rentbw_tester t; + t.produce_block(); + + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("rentbw hasn't been initialized"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 4, rentbw_frac / 8, + asset::from_string("1.000 TST"))); + + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { + config.net.current_weight_ratio = rentbw_frac; + config.net.target_weight_ratio = rentbw_frac; + config.net.assumed_stake_weight = stake_weight; + config.net.exponent = 1; + config.net.target_price = asset::from_string("1000000.0000 TST"); + + config.cpu.current_weight_ratio = rentbw_frac; + config.cpu.target_weight_ratio = rentbw_frac; + config.cpu.assumed_stake_weight = stake_weight; + config.cpu.exponent = 1; + config.cpu.target_price = asset::from_string("1000000.0000 TST"); + + config.rent_days = 30; + config.min_rent_price = asset::from_string("1.0000 TST"); + }))); + + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("max_payment doesn't match core symbol"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.000 TST"))); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("market doesn't have resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, 0, rentbw_frac, asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("market doesn't have resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, 0, asset::from_string("1.0000 TST"))); + + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { + // weight = stake_weight * 3/4 + config.net.current_weight_ratio = rentbw_frac / 4; + config.net.target_weight_ratio = rentbw_frac / 4; + config.net.assumed_stake_weight = stake_weight; + config.net.exponent = 2; + config.net.target_price = asset::from_string("1000000.0000 TST"); + + // weight = stake_weight * 4/5 * 1/2 + config.cpu.current_weight_ratio = rentbw_frac / 5; + config.cpu.target_weight_ratio = rentbw_frac / 5; + config.cpu.assumed_stake_weight = stake_weight / 2; + config.cpu.exponent = 3; + config.cpu.target_price = asset::from_string("2000000.0000 TST"); + + config.rent_days = 30; + config.min_rent_price = asset::from_string("1.0000 TST"); + }))); + + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("calculated fee exceeds max_payment"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("can't channel fees to rex"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, + asset::from_string("3000000.0000 TST"))); + + t.start_rex(); + t.create_account_with_resources(N(aaaaaaaaaaaa), config::system_account_name, core_sym::from_string("1.0000"), false, + core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + t.create_account_with_resources(N(bbbbbbbbbbbb), config::system_account_name, core_sym::from_string("1.0000"), false, + core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3000000.0000")); + BOOST_REQUIRE_EQUAL("", t.rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, + asset::from_string("3000000.0000 TST"))); + + // todo: calculated fee is below minimum; try renting more + +} // rent_tests +FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() From e6d662212443bdd9a652e0837c4448a60b41f1e0 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 6 Dec 2019 16:57:24 -0500 Subject: [PATCH 16/45] tests --- tests/eosio.rentbw_tests.cpp | 210 ++++++++++++++++++++++------------- 1 file changed, 131 insertions(+), 79 deletions(-) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index ee9fa7208..ee1d7ce7e 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -13,7 +13,7 @@ #include "eosio.system_tester.hpp" inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 -inline constexpr int64_t stake_weight = 1000000000000ll; // 10^12 +inline constexpr int64_t stake_weight = 100'000'000'0000ll; // 10^12 struct rentbw_config_resource { int64_t current_weight_ratio = {}; @@ -162,25 +162,40 @@ struct rentbw_tester : eosio_system_tester { }; void check_rentbw(const name& payer, const name& receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, - const asset& max_payment) { + const asset& expected_fee, int64_t expected_net, int64_t expected_cpu) { auto before_payer = get_account_info(payer); auto before_receiver = get_account_info(receiver); auto before_state = get_state(); - BOOST_REQUIRE_EQUAL("", rentbw(payer, receiver, days, net_frac, cpu_frac, max_payment)); + BOOST_REQUIRE_EQUAL("", rentbw(payer, receiver, days, net_frac, cpu_frac, expected_fee)); auto after_payer = get_account_info(payer); auto after_receiver = get_account_info(receiver); auto after_state = get_state(); + if (false) { + ilog("before_state.net.assumed_stake_weight: ${x}", ("x", before_state.net.assumed_stake_weight)); + ilog("before_state.net.weight_ratio: ${x}", + ("x", before_state.net.weight_ratio / double(rentbw_frac))); + ilog("before_state.net.assumed_stake_weight: ${x}", ("x", before_state.net.assumed_stake_weight)); + ilog("before_state.net.weight: ${x}", ("x", before_state.net.weight)); + + ilog("before_receiver.net: ${x}", ("x", before_receiver.net)); + ilog("after_receiver.net: ${x}", ("x", after_receiver.net)); + ilog("after_receiver.net - before_receiver.net: ${x}", ("x", after_receiver.net - before_receiver.net)); + ilog("expected_net: ${x}", ("x", expected_net)); + ilog("before_payer.liquid - after_payer.liquid: ${x}", ("x", before_payer.liquid - after_payer.liquid)); + ilog("expected_fee: ${x}", ("x", expected_fee)); + } + if (payer != receiver) { - BOOST_REQUIRE(before_payer.ram == after_payer.ram); - BOOST_REQUIRE(before_payer.net == after_payer.net); - BOOST_REQUIRE(before_payer.cpu == after_payer.cpu); - BOOST_REQUIRE(before_receiver.liquid == after_receiver.liquid); + BOOST_REQUIRE_EQUAL(before_payer.ram, after_payer.ram); + BOOST_REQUIRE_EQUAL(before_payer.net, after_payer.net); + BOOST_REQUIRE_EQUAL(before_payer.cpu, after_payer.cpu); + BOOST_REQUIRE_EQUAL(before_receiver.liquid, after_receiver.liquid); } - BOOST_REQUIRE(before_receiver.ram == after_receiver.ram); - // BOOST_REQUIRE(before_receiver.net == after_receiver.net); - // BOOST_REQUIRE(before_receiver.cpu == after_receiver.cpu); - // BOOST_REQUIRE(before_payer.liquid == after_payer.liquid); + BOOST_REQUIRE_EQUAL(before_receiver.ram, after_receiver.ram); + BOOST_REQUIRE_EQUAL(before_receiver.net, after_receiver.net - expected_net); + BOOST_REQUIRE_EQUAL(before_receiver.cpu, after_receiver.cpu - expected_cpu); + BOOST_REQUIRE_EQUAL(before_payer.liquid - after_payer.liquid, expected_fee); } }; @@ -389,78 +404,115 @@ BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_CASE(rent_tests) try { - rentbw_tester t; - t.produce_block(); - - BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("rentbw hasn't been initialized"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 4, rentbw_frac / 8, - asset::from_string("1.000 TST"))); - - BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { - config.net.current_weight_ratio = rentbw_frac; - config.net.target_weight_ratio = rentbw_frac; - config.net.assumed_stake_weight = stake_weight; - config.net.exponent = 1; - config.net.target_price = asset::from_string("1000000.0000 TST"); - - config.cpu.current_weight_ratio = rentbw_frac; - config.cpu.target_weight_ratio = rentbw_frac; - config.cpu.assumed_stake_weight = stake_weight; - config.cpu.exponent = 1; - config.cpu.target_price = asset::from_string("1000000.0000 TST"); - - config.rent_days = 30; - config.min_rent_price = asset::from_string("1.0000 TST"); - }))); - - BOOST_REQUIRE_EQUAL( - t.wasm_assert_msg("max_payment doesn't match core symbol"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.000 TST"))); - BOOST_REQUIRE_EQUAL( - t.wasm_assert_msg("market doesn't have resources available"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, 0, rentbw_frac, asset::from_string("1.0000 TST"))); - BOOST_REQUIRE_EQUAL( - t.wasm_assert_msg("market doesn't have resources available"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, 0, asset::from_string("1.0000 TST"))); - - BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { - // weight = stake_weight * 3/4 - config.net.current_weight_ratio = rentbw_frac / 4; - config.net.target_weight_ratio = rentbw_frac / 4; - config.net.assumed_stake_weight = stake_weight; - config.net.exponent = 2; - config.net.target_price = asset::from_string("1000000.0000 TST"); - - // weight = stake_weight * 4/5 * 1/2 - config.cpu.current_weight_ratio = rentbw_frac / 5; - config.cpu.target_weight_ratio = rentbw_frac / 5; - config.cpu.assumed_stake_weight = stake_weight / 2; - config.cpu.exponent = 3; - config.cpu.target_price = asset::from_string("2000000.0000 TST"); - - config.rent_days = 30; - config.min_rent_price = asset::from_string("1.0000 TST"); - }))); - - BOOST_REQUIRE_EQUAL( - t.wasm_assert_msg("calculated fee exceeds max_payment"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); - BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("can't channel fees to rex"), // - t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, - asset::from_string("3000000.0000 TST"))); + { + rentbw_tester t; + t.produce_block(); + + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("rentbw hasn't been initialized"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 4, rentbw_frac / 8, + asset::from_string("1.000 TST"))); + + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { + config.net.current_weight_ratio = rentbw_frac; + config.net.target_weight_ratio = rentbw_frac; + config.net.assumed_stake_weight = stake_weight; + config.net.exponent = 1; + config.net.target_price = asset::from_string("1000000.0000 TST"); + + config.cpu.current_weight_ratio = rentbw_frac; + config.cpu.target_weight_ratio = rentbw_frac; + config.cpu.assumed_stake_weight = stake_weight; + config.cpu.exponent = 1; + config.cpu.target_price = asset::from_string("1000000.0000 TST"); + + config.rent_days = 30; + config.min_rent_price = asset::from_string("1.0000 TST"); + }))); + + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("max_payment doesn't match core symbol"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.000 TST"))); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("market doesn't have resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, 0, rentbw_frac, asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("market doesn't have resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, 0, asset::from_string("1.0000 TST"))); + } - t.start_rex(); - t.create_account_with_resources(N(aaaaaaaaaaaa), config::system_account_name, core_sym::from_string("1.0000"), false, - core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); - t.create_account_with_resources(N(bbbbbbbbbbbb), config::system_account_name, core_sym::from_string("1.0000"), false, - core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + auto init = [](auto& t, bool rex) { + t.produce_block(); + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_config([&](auto& config) { + // weight = stake_weight * 3 + config.net.current_weight_ratio = rentbw_frac / 4; + config.net.target_weight_ratio = rentbw_frac / 4; + config.net.assumed_stake_weight = stake_weight; + config.net.exponent = 2; + config.net.target_price = asset::from_string("1000000.0000 TST"); + + // weight = stake_weight * 4 / 2 + config.cpu.current_weight_ratio = rentbw_frac / 5; + config.cpu.target_weight_ratio = rentbw_frac / 5; + config.cpu.assumed_stake_weight = stake_weight / 2; + config.cpu.exponent = 3; + config.cpu.target_price = asset::from_string("2000000.0000 TST"); + + config.rent_days = 30; + config.min_rent_price = asset::from_string("1.0000 TST"); + }))); + + if (rex) + t.start_rex(); + + t.create_account_with_resources(N(aaaaaaaaaaaa), config::system_account_name, core_sym::from_string("1.0000"), + false, core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + t.create_account_with_resources(N(bbbbbbbbbbbb), config::system_account_name, core_sym::from_string("1.0000"), + false, core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + }; + auto net_weight = stake_weight * 3; + auto cpu_weight = stake_weight * 4 / 2; - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3000000.0000")); - BOOST_REQUIRE_EQUAL("", t.rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, - asset::from_string("3000000.0000 TST"))); + { + rentbw_tester t; + init(t, false); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("calculated fee exceeds max_payment"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("can't channel fees to rex"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, + asset::from_string("3000000.0000 TST"))); + } - // todo: calculated fee is below minimum; try renting more + // net:100%, cpu:100% + { + rentbw_tester t; + init(t, true); + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3000000.0000")); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("calculated fee is below minimum; try renting more"), + t.rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, 10, 10, asset::from_string("3000000.0000 TST"))); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, + asset::from_string("3000000.0000 TST"), net_weight, cpu_weight); + } + // net:30%, cpu:40%, then net:5%, cpu:10% + { + rentbw_tester t; + init(t, true); + // (.3 ^ 2) * 1000000.0000 = 90000.0000 + // (.4 ^ 3) * 2000000.0000 = 128000.0000 + // total = 218000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("218000.0001")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .3, rentbw_frac * .4, + asset::from_string("218000.0001 TST"), net_weight * .3, cpu_weight * .4); + + // (.35 ^ 2) * 1000000.0000 - 90000.0000 = 32500.0000 + // (.5 ^ 3) * 2000000.0000 - 128000.0000 = 122000.0000 + // total = 154500.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("154499.9999")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .05, rentbw_frac * .10, + asset::from_string("154499.9999 TST"), net_weight * .05, cpu_weight * .10); + } } // rent_tests FC_LOG_AND_RETHROW() From c9e38e38aeb6194f60f4256bee6debb295df96ed Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 6 Dec 2019 17:36:09 -0500 Subject: [PATCH 17/45] Fix eosio.reserve --- contracts/eosio.system/src/rentbw.cpp | 2 ++ tests/eosio.rentbw_tests.cpp | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 73d5da5b0..fbf13566b 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -257,6 +257,8 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d order.cpu_weight = cpu_amount; order.expires = now + eosio::days(days); }); + net_delta_available -= net_amount; + cpu_delta_available -= cpu_amount; adjust_resources(payer, receiver, core_symbol, net_amount, cpu_amount, true); adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index ee1d7ce7e..23f85defa 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -165,13 +165,15 @@ struct rentbw_tester : eosio_system_tester { const asset& expected_fee, int64_t expected_net, int64_t expected_cpu) { auto before_payer = get_account_info(payer); auto before_receiver = get_account_info(receiver); + auto before_reserve = get_account_info(N(eosio.reserv)); auto before_state = get_state(); BOOST_REQUIRE_EQUAL("", rentbw(payer, receiver, days, net_frac, cpu_frac, expected_fee)); auto after_payer = get_account_info(payer); auto after_receiver = get_account_info(receiver); + auto after_reserve = get_account_info(N(eosio.reserv)); auto after_state = get_state(); - if (false) { + if (true) { ilog("before_state.net.assumed_stake_weight: ${x}", ("x", before_state.net.assumed_stake_weight)); ilog("before_state.net.weight_ratio: ${x}", ("x", before_state.net.weight_ratio / double(rentbw_frac))); @@ -184,6 +186,11 @@ struct rentbw_tester : eosio_system_tester { ilog("expected_net: ${x}", ("x", expected_net)); ilog("before_payer.liquid - after_payer.liquid: ${x}", ("x", before_payer.liquid - after_payer.liquid)); ilog("expected_fee: ${x}", ("x", expected_fee)); + + ilog("before_reserve.net: ${x}", ("x", before_reserve.net)); + ilog("after_reserve.net: ${x}", ("x", after_reserve.net)); + ilog("before_reserve.cpu: ${x}", ("x", before_reserve.cpu)); + ilog("after_reserve.cpu: ${x}", ("x", after_reserve.cpu)); } if (payer != receiver) { @@ -193,9 +200,12 @@ struct rentbw_tester : eosio_system_tester { BOOST_REQUIRE_EQUAL(before_receiver.liquid, after_receiver.liquid); } BOOST_REQUIRE_EQUAL(before_receiver.ram, after_receiver.ram); - BOOST_REQUIRE_EQUAL(before_receiver.net, after_receiver.net - expected_net); - BOOST_REQUIRE_EQUAL(before_receiver.cpu, after_receiver.cpu - expected_cpu); + BOOST_REQUIRE_EQUAL(after_receiver.net - before_receiver.net, expected_net); + BOOST_REQUIRE_EQUAL(after_receiver.cpu - before_receiver.cpu, expected_cpu); BOOST_REQUIRE_EQUAL(before_payer.liquid - after_payer.liquid, expected_fee); + + BOOST_REQUIRE_EQUAL(before_reserve.net - after_reserve.net, expected_net); + BOOST_REQUIRE_EQUAL(before_reserve.cpu - after_reserve.cpu, expected_cpu); } }; From 469c56e1d4040f53b835d5c97c3126ba8b17fe67 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 9 Dec 2019 11:40:33 -0500 Subject: [PATCH 18/45] fix utilization update order --- contracts/eosio.system/src/rentbw.cpp | 15 +++++++-------- tests/eosio.rentbw_tests.cpp | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index fbf13566b..f40db80e9 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -3,6 +3,9 @@ namespace eosiosystem { +void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delta_available); +void update_utilization(time_point_sec now, rentbw_state_resource& res); + void system_contract::adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, int64_t cpu_delta, bool must_not_be_managed) { if (!net_delta && !cpu_delta) @@ -57,6 +60,8 @@ void system_contract::adjust_resources(name payer, name account, symbol core_sym void system_contract::process_rentbw_queue(time_point_sec now, symbol core_symbol, rentbw_state& state, rentbw_order_table& orders, uint32_t max_items, int64_t& net_delta_available, int64_t& cpu_delta_available) { + update_utilization(now, state.net); + update_utilization(now, state.cpu); auto idx = orders.get_index<"byexpires"_n>(); while (max_items--) { auto it = idx.begin(); @@ -69,6 +74,8 @@ void system_contract::process_rentbw_queue(time_point_sec now, symbol core_symbo } state.net.utilization -= net_delta_available; state.cpu.utilization -= cpu_delta_available; + update_weight(now, state.net, net_delta_available); + update_weight(now, state.cpu, cpu_delta_available); } void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delta_available) { @@ -196,10 +203,6 @@ void system_contract::rentbwexec(const name& user, uint16_t max) { int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; process_rentbw_queue(now, core_symbol, state, orders, max, net_delta_available, cpu_delta_available); - update_weight(now, state.net, net_delta_available); - update_weight(now, state.cpu, cpu_delta_available); - update_utilization(now, state.net); - update_utilization(now, state.cpu); adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); state_sing.set(state, get_self()); @@ -224,10 +227,6 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; process_rentbw_queue(now, core_symbol, state, orders, 2, net_delta_available, cpu_delta_available); - update_weight(now, state.net, net_delta_available); - update_weight(now, state.cpu, cpu_delta_available); - update_utilization(now, state.net); - update_utilization(now, state.cpu); eosio::asset fee{ 0, core_symbol }; auto process = [&](int64_t frac, int64_t& amount, rentbw_state_resource& state) { diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 23f85defa..b2705b1c3 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -173,7 +173,7 @@ struct rentbw_tester : eosio_system_tester { auto after_reserve = get_account_info(N(eosio.reserv)); auto after_state = get_state(); - if (true) { + if (false) { ilog("before_state.net.assumed_stake_weight: ${x}", ("x", before_state.net.assumed_stake_weight)); ilog("before_state.net.weight_ratio: ${x}", ("x", before_state.net.weight_ratio / double(rentbw_frac))); @@ -206,6 +206,8 @@ struct rentbw_tester : eosio_system_tester { BOOST_REQUIRE_EQUAL(before_reserve.net - after_reserve.net, expected_net); BOOST_REQUIRE_EQUAL(before_reserve.cpu - after_reserve.cpu, expected_cpu); + BOOST_REQUIRE_EQUAL(after_state.net.utilization - before_state.net.utilization, expected_net); + BOOST_REQUIRE_EQUAL(after_state.cpu.utilization - before_state.cpu.utilization, expected_cpu); } }; From 1b28e3bd683b7a3a13d003715864ca6717c852e3 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 9 Dec 2019 17:48:14 -0500 Subject: [PATCH 19/45] Fix adjusted_utilization --- contracts/eosio.system/src/rentbw.cpp | 2 +- tests/eosio.rentbw_tests.cpp | 69 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index f40db80e9..9c6699a62 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -99,7 +99,7 @@ void update_utilization(time_point_sec now, rentbw_state_resource& res) { res.adjusted_utilization = // res.utilization + (res.adjusted_utilization - res.utilization) * - exp((now.utc_seconds - res.utilization_timestamp.utc_seconds) / double(-res.decay_secs)); + exp(-double(now.utc_seconds - res.utilization_timestamp.utc_seconds) / double(res.decay_secs)); res.utilization_timestamp = now; } diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index b2705b1c3..1ae7872ad 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -525,6 +525,75 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .05, rentbw_frac * .10, asset::from_string("154499.9999 TST"), net_weight * .05, cpu_weight * .10); } + + { + // net:100%, cpu:100% + rentbw_tester t; + init(t, true); + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3000000.0000")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, + asset::from_string("3000000.0000 TST"), net_weight, cpu_weight); + + // No more available for 30 days + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + t.produce_block(fc::days(29)); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + t.produce_block(fc::days(1) - fc::milliseconds(1500)); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + t.produce_block(fc::milliseconds(500)); + + // immediate renewal: adjusted_utilization doesn't have time to fall + // + // (2.0 ^ 2) * 1000000.0000 - 1000000.0000 = 3000000.0000 + // (2.0 ^ 3) * 2000000.0000 - 2000000.0000 = 14000000.0000 + // total = 17000000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("17000000.0000")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, + asset::from_string("17000000.0000 TST"), 0, 0); + + // No more available for 30 days + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + t.produce_block(fc::days(29)); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + t.produce_block(fc::days(1) - fc::milliseconds(1000)); + BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac / 1000, rentbw_frac / 1000, + asset::from_string("1.0000 TST"))); + + // Start decay + t.produce_block(fc::milliseconds(1000)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, net_weight, net_weight / 1000)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, cpu_weight, cpu_weight / 1000)); + + // 1 day of decay + t.produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, int64_t(net_weight * exp(-1)), + int64_t(net_weight * exp(-1)) / 1000)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, int64_t(cpu_weight * exp(-1)), + int64_t(cpu_weight * exp(-1)) / 1000)); + + // 1 day of decay + t.produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, int64_t(net_weight * exp(-2)), + int64_t(net_weight * exp(-2)) / 1000)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, int64_t(cpu_weight * exp(-2)), + int64_t(cpu_weight * exp(-2)) / 1000)); + } + } // rent_tests FC_LOG_AND_RETHROW() From 015ded88c4c91eab6858758a8f1614694408bbc1 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 9 Dec 2019 19:03:30 -0500 Subject: [PATCH 20/45] tests --- tests/eosio.rentbw_tests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 1ae7872ad..a92dcf0fd 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -592,6 +592,15 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { int64_t(net_weight * exp(-2)) / 1000)); BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, int64_t(cpu_weight * exp(-2)), int64_t(cpu_weight * exp(-2)) / 1000)); + + // 100% after 2 days of decay + // + // [((e^-2 + 1.0) ^ 2) - ((e^-2) ^ 2) ] * 1000000.0000 = 1270670.5664 + // [((e^-2 + 1.0) ^ 3) - ((e^-2) ^ 3) ] * 2000000.0000 = 2921905.5327 + // total = 4192576.0991 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("4192561.0244")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, + asset::from_string("4192561.0244 TST"), net_weight, cpu_weight); } } // rent_tests From 5d712b6d09b0f057c1b17a735ae9bd6d23da33b3 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 10 Dec 2019 13:36:22 -0500 Subject: [PATCH 21/45] tests --- tests/eosio.rentbw_tests.cpp | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index a92dcf0fd..fece8839f 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -603,6 +603,52 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { asset::from_string("4192561.0244 TST"), net_weight, cpu_weight); } + { + rentbw_tester t; + init(t, true); + + // 10%, 20% + // (.1 ^ 2) * 1000000.0000 = 10000.0000 + // (.2 ^ 3) * 2000000.0000 = 16000.0000 + // total = 26000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("26000.0002")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .1, rentbw_frac * .2, + asset::from_string("26000.0002 TST"), net_weight * .1, cpu_weight * .2); + + t.produce_block(fc::days(15) - fc::milliseconds(500)); + + // 20%, 20% + // (.3 ^ 2) * 1000000.0000 - 10000.0000 = 80000.0000 + // (.4 ^ 3) * 2000000.0000 - 16000.0000 = 112000.0000 + // total = 192000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("191999.9999")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .2, rentbw_frac * .2, + asset::from_string("191999.9999 TST"), net_weight * .2, cpu_weight * .2); + + // Start decay + t.produce_block(fc::days(15) - fc::milliseconds(1000)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, .3 * net_weight, 0)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, .4 * cpu_weight, 0)); + + // 1 day of decay from (30%, 40%) to (20%, 20%) + t.produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE( + near(t.get_state().net.adjusted_utilization, int64_t(.1 * net_weight * exp(-1) + .2 * net_weight), 0)); + BOOST_REQUIRE( + near(t.get_state().cpu.adjusted_utilization, int64_t(.2 * cpu_weight * exp(-1) + .2 * cpu_weight), 0)); + + // 2 days of decay from (30%, 40%) to (20%, 20%) + t.produce_block(fc::days(1) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE( + near(t.get_state().net.adjusted_utilization, int64_t(.1 * net_weight * exp(-2) + .2 * net_weight), 0)); + BOOST_REQUIRE( + near(t.get_state().cpu.adjusted_utilization, int64_t(.2 * cpu_weight * exp(-2) + .2 * cpu_weight), 0)); + } + } // rent_tests FC_LOG_AND_RETHROW() From 0fca4739f731743579bd683f16714b8167f8baa7 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 10 Dec 2019 18:13:23 -0500 Subject: [PATCH 22/45] prevent weight from getting too large --- contracts/eosio.system/src/rentbw.cpp | 5 +++-- tests/eosio.rentbw_tests.cpp | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 9c6699a62..4476b5ba2 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -86,7 +86,6 @@ void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delt int128_t(res.target_weight_ratio - res.initial_weight_ratio) * (now.utc_seconds - res.initial_timestamp.utc_seconds) / (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); - // !!! check bounds of weight_ratio int64_t new_weight = res.assumed_stake_weight * int128_t(rentbw_frac) / res.weight_ratio - res.assumed_stake_weight; delta_available += new_weight - res.weight; res.weight = new_weight; @@ -137,7 +136,6 @@ void system_contract::configrentbw(rentbw_config& args) { if (!args.target_price.amount && state.target_price.amount) args.target_price = state.target_price; - // !!! examine checks if (args.current_weight_ratio == args.target_weight_ratio) args.target_timestamp = now; else @@ -148,6 +146,9 @@ void system_contract::configrentbw(rentbw_config& args) { eosio::check(args.target_weight_ratio <= args.current_weight_ratio, "weight can't grow over time"); eosio::check(args.assumed_stake_weight >= 1, "assumed_stake_weight must be at least 1; a much larger value is recommended"); + eosio::check(args.assumed_stake_weight * int128_t(rentbw_frac) / args.target_weight_ratio <= + std::numeric_limits::max(), + "assumed_stake_weight/target_weight_ratio is too large"); eosio::check(args.exponent >= 1, "exponent must be >= 1"); eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); eosio::check(args.target_price.symbol == core_symbol, "target_price doesn't match core symbol"); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index fece8839f..04b10437e 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -239,6 +239,11 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { // net assertions BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), configbw(make_config([](auto& c) { c.net.current_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight/target_weight_ratio is too large"), + configbw(make_config([](auto& c) { + c.net.assumed_stake_weight = 100000; + c.net.target_weight_ratio = 10; + }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), configbw(make_config([](auto& c) { c.net.target_weight_ratio = rentbw_frac + 1; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), @@ -263,6 +268,11 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { // cpu assertions BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), configbw(make_config([](auto& c) { c.cpu.current_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight/target_weight_ratio is too large"), + configbw(make_config([](auto& c) { + c.cpu.assumed_stake_weight = 100000; + c.cpu.target_weight_ratio = 10; + }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), configbw(make_config([](auto& c) { c.cpu.target_weight_ratio = rentbw_frac + 1; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), @@ -283,11 +293,19 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); - - // TODO: "weight can't shrink below utilization" } // config_tests FC_LOG_AND_RETHROW() +/* TODO: + +eosio::check(days == state.rent_days, "days doesn't match configuration"); +eosio::check(net_frac >= 0, "net_frac can't be negative"); +eosio::check(cpu_frac >= 0, "cpu_frac can't be negative"); +eosio::check(net_frac <= rentbw_frac, "net can't be more than 100%"); +eosio::check(cpu_frac <= rentbw_frac, "cpu can't be more than 100%"); +eosio::check(state.net.weight >= state.net.utilization, "weight can't shrink below utilization"); +*/ + BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { produce_block(); From 7e02260a607e28bd4061300daaf25cb5a829ead7 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 11 Dec 2019 11:20:12 -0500 Subject: [PATCH 23/45] tests --- tests/eosio.rentbw_tests.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 04b10437e..6af225234 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -298,11 +298,6 @@ FC_LOG_AND_RETHROW() /* TODO: -eosio::check(days == state.rent_days, "days doesn't match configuration"); -eosio::check(net_frac >= 0, "net_frac can't be negative"); -eosio::check(cpu_frac >= 0, "cpu_frac can't be negative"); -eosio::check(net_frac <= rentbw_frac, "net can't be more than 100%"); -eosio::check(cpu_frac <= rentbw_frac, "cpu can't be more than 100%"); eosio::check(state.net.weight >= state.net.utilization, "weight can't shrink below utilization"); */ @@ -505,6 +500,25 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { { rentbw_tester t; init(t, false); + BOOST_REQUIRE_EQUAL( + t.wasm_assert_msg("days doesn't match configuration"), // + t.rentbw(N(bob111111111), N(alice1111111), 20, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("net_frac can't be negative"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, -rentbw_frac, rentbw_frac, + asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("cpu_frac can't be negative"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, -rentbw_frac, + asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("net can't be more than 100%"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac + 1, rentbw_frac, + asset::from_string("1.0000 TST"))); + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("cpu can't be more than 100%"), // + t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac + 1, + asset::from_string("1.0000 TST"))); BOOST_REQUIRE_EQUAL( t.wasm_assert_msg("calculated fee exceeds max_payment"), // t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); From 98ab2b35aafc8a9e583edbc75272e00138cae298 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 11 Dec 2019 11:35:42 -0500 Subject: [PATCH 24/45] tests --- tests/eosio.rentbw_tests.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 6af225234..4306ce383 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -296,11 +296,6 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { } // config_tests FC_LOG_AND_RETHROW() -/* TODO: - -eosio::check(state.net.weight >= state.net.utilization, "weight can't shrink below utilization"); -*/ - BOOST_FIXTURE_TEST_CASE(weight_tests, rentbw_tester) try { produce_block(); @@ -537,6 +532,31 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { t.rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, 10, 10, asset::from_string("3000000.0000 TST"))); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, asset::from_string("3000000.0000 TST"), net_weight, cpu_weight); + + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("weight can't shrink below utilization"), + t.configbw(t.make_default_config([&](auto& config) { + config.net.current_weight_ratio = rentbw_frac / 4 + 1; + config.net.target_weight_ratio = rentbw_frac / 4 + 1; + config.cpu.current_weight_ratio = rentbw_frac / 5; + config.cpu.target_weight_ratio = rentbw_frac / 5; + }))); + BOOST_REQUIRE_EQUAL( // + t.wasm_assert_msg("weight can't shrink below utilization"), + t.configbw(t.make_default_config([&](auto& config) { + config.net.current_weight_ratio = rentbw_frac / 4; + config.net.target_weight_ratio = rentbw_frac / 4; + config.cpu.current_weight_ratio = rentbw_frac / 5 + 1; + config.cpu.target_weight_ratio = rentbw_frac / 5 + 1; + }))); + BOOST_REQUIRE_EQUAL( // + "", // + t.configbw(t.make_default_config([&](auto& config) { + config.net.current_weight_ratio = rentbw_frac / 4; + config.net.target_weight_ratio = rentbw_frac / 4; + config.cpu.current_weight_ratio = rentbw_frac / 5; + config.cpu.target_weight_ratio = rentbw_frac / 5; + }))); } // net:30%, cpu:40%, then net:5%, cpu:10% From 6bbfe2a8e17f30dc9754e2729e7e8a26cb769907 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 11 Dec 2019 13:04:55 -0500 Subject: [PATCH 25/45] Missing action wrapper --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 665e5de46..6a6eddd42 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -1227,6 +1227,7 @@ namespace eosiosystem { using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; using configcpu_action = eosio::action_wrapper<"configrentbw"_n, &system_contract::configrentbw>; + using rentbwexec_action = eosio::action_wrapper<"rentbwexec"_n, &system_contract::rentbwexec>; using rentbw_action = eosio::action_wrapper<"rentbw"_n, &system_contract::rentbw>; private: From 14a65eb9591b4fd64afe0da9acb542446ad91c21 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 13 Dec 2019 10:41:02 -0500 Subject: [PATCH 26/45] Adjust min_rent_price description --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 6a6eddd42..faf124a58 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -457,8 +457,8 @@ namespace eosiosystem { rentbw_config_resource cpu; // CPU market configuration uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the // existing setting. - asset min_rent_price; // Rents below this amount are rejected. This needs to be large enough to cover - // RAM costs. Set this to 0 to preserve the existing setting. + asset min_rent_price; // Rents below this amount are rejected. Set this to 0 to preserve the + // existing setting. }; struct rentbw_state_resource { From 921cb358b5f7629460b026ec903b4cc141ed31f0 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 16 Dec 2019 20:55:31 -0500 Subject: [PATCH 27/45] minor changes to rentbw system --- .../include/eosio.system/eosio.system.hpp | 13 +++--- contracts/eosio.system/src/rentbw.cpp | 44 ++++++++++++++----- contracts/eosio.system/src/rex.cpp | 1 + 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index faf124a58..d812f5b43 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -439,15 +439,16 @@ namespace eosiosystem { // total rented by REX at the time the rentbw market is first activated. Set // this to 0 to preserve the existing setting; this avoids sudden price jumps. // For new chains which don't need to phase out staking and REX, 10^12 is - // probably a good value. + // probably a good value. time_point_sec target_timestamp; // Stop automatic weight_ratio shrinkage at this time. Once this // time hits, weight_ratio will be target_weight_ratio. Ignored if // current_weight_ratio == target_weight_ratio. Set this to 0 to preserve the // existing setting. double exponent; // Exponent of resource price curve. Must be >= 1. Set this to 0 to preserve the // existing setting. - uint32_t decay_secs; // Number of seconds for adjusted resource utilization to decay to instantaneous - // utilization within exp(-1). Set this to 0 to preserve the existing setting. + uint32_t decay_secs; // Number of seconds for the gap between adjusted resource utilization and + // instantaneous utilization to shrink by 63%. Set this to 0 to preserve the + // existing setting. asset target_price; // Fee needed to rent the entire resource market weight. Set this to 0 to // preserve the existing setting. }; @@ -481,8 +482,8 @@ namespace eosiosystem { asset target_price = {}; // Fee needed to rent the entire resource market weight. int64_t utilization = 0; // Instantaneous resource utilization. This is the current // amount sold. utilization <= weight. - int64_t adjusted_utilization = 0; // Adjusted resource utilization. This >= utilization. It - // grows instantly but decays exponentially. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This is >= utilization and + // <= weight. It grows instantly but decays exponentially. time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; @@ -1169,7 +1170,7 @@ namespace eosiosystem { /** * Rent NET and CPU - * + * * @param payer - the resource buyer * @param receiver - the resource receiver * @param days - number of days of resource availability. Must match market configuration. diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 4476b5ba2..4b2af9db1 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -1,9 +1,18 @@ #include -#include +#include +#include +#include namespace eosiosystem { void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delta_available); + +/** + * @pre now >= res.utilization_timestamp + * @post res.utilization <= new res.adjusted_utilization + * @post if res.utilization < old res.adjusted_utilization, then new res.adjusted_utilization <= old res.adjusted_utilization + * @post if res.utilization >= old res.adjusted_utilization, then new res.adjusted_utilization == res.utilization + */ void update_utilization(time_point_sec now, rentbw_state_resource& res); void system_contract::adjust_resources(name payer, name account, symbol core_symbol, int64_t net_delta, @@ -79,26 +88,30 @@ void system_contract::process_rentbw_queue(time_point_sec now, symbol core_symbo } void update_weight(time_point_sec now, rentbw_state_resource& res, int64_t& delta_available) { - if (now >= res.target_timestamp) + if (now >= res.target_timestamp) { res.weight_ratio = res.target_weight_ratio; - else + } else { res.weight_ratio = res.initial_weight_ratio + // int128_t(res.target_weight_ratio - res.initial_weight_ratio) * (now.utc_seconds - res.initial_timestamp.utc_seconds) / (res.target_timestamp.utc_seconds - res.initial_timestamp.utc_seconds); - int64_t new_weight = res.assumed_stake_weight * int128_t(rentbw_frac) / res.weight_ratio - res.assumed_stake_weight; + } + int64_t new_weight = res.assumed_stake_weight * int128_t(rentbw_frac) / res.weight_ratio - res.assumed_stake_weight; delta_available += new_weight - res.weight; res.weight = new_weight; } void update_utilization(time_point_sec now, rentbw_state_resource& res) { - if (res.utilization >= res.adjusted_utilization) + if (now <= res.utilization_timestamp) return; + + if (res.utilization >= res.adjusted_utilization) { res.adjusted_utilization = res.utilization; - else - res.adjusted_utilization = // - res.utilization + - (res.adjusted_utilization - res.utilization) * - exp(-double(now.utc_seconds - res.utilization_timestamp.utc_seconds) / double(res.decay_secs)); + } else { + int64_t diff = res.adjusted_utilization - res.utilization; + int64_t delta = diff * std::exp(-double(now.utc_seconds - res.utilization_timestamp.utc_seconds) / double(res.decay_secs)); + delta = std::clamp( delta, 0ll, diff); + res.adjusted_utilization = res.utilization + delta; + } res.utilization_timestamp = now; } @@ -109,11 +122,18 @@ void system_contract::configrentbw(rentbw_config& args) { rentbw_state_singleton state_sing{ get_self(), 0 }; auto state = state_sing.get_or_default(); + eosio::check(eosio::is_account(reserv_account), "eosio.reserv account must first be created"); + int64_t net_delta_available = 0; int64_t cpu_delta_available = 0; if (state_sing.exists()) { + update_utilization(now, state.net); + update_utilization(now, state.cpu); update_weight(now, state.net, net_delta_available); update_weight(now, state.cpu, cpu_delta_available); + } else { + state.net.utilization_timestamp = now; + state.cpu.utilization_timestamp = now; } auto update = [&](auto& state, auto& args) { @@ -183,13 +203,15 @@ void system_contract::configrentbw(rentbw_config& args) { update_weight(now, state.cpu, cpu_delta_available); eosio::check(state.net.weight >= state.net.utilization, "weight can't shrink below utilization"); eosio::check(state.cpu.weight >= state.cpu.utilization, "weight can't shrink below utilization"); + state.net.adjusted_utilization = std::min(state.net.adjusted_utilization, state.net.weight); + state.cpu.adjusted_utilization = std::min(state.cpu.adjusted_utilization, state.cpu.weight); adjust_resources(get_self(), reserv_account, core_symbol, net_delta_available, cpu_delta_available, true); state_sing.set(state, get_self()); } // system_contract::configrentbw int64_t calc_rentbw_price(const rentbw_state_resource& state, double utilization) { - return ceil(state.target_price.amount * pow(utilization / state.weight, state.exponent)); + return std::ceil(state.target_price.amount * std::pow(utilization / state.weight, state.exponent)); } void system_contract::rentbwexec(const name& user, uint16_t max) { diff --git a/contracts/eosio.system/src/rex.cpp b/contracts/eosio.system/src/rex.cpp index dc09be6de..ee1240e9d 100644 --- a/contracts/eosio.system/src/rex.cpp +++ b/contracts/eosio.system/src/rex.cpp @@ -813,6 +813,7 @@ namespace eosiosystem { * * @param from - account from which asset is transfered to REX pool * @param amount - amount of tokens to be transfered + * @param required - if true, asserts when the system is not configured to channel fees into REX */ void system_contract::channel_to_rex( const name& from, const asset& amount, bool required ) { From ee5a971fa8a34e3944d6c4477fe0615afbba00a3 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 16 Dec 2019 22:14:36 -0500 Subject: [PATCH 28/45] also change description of decay_secs in rentbw_state_resource to reflect the change in rentbw_config_resource --- .../eosio.system/include/eosio.system/eosio.system.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 5888e0e22..6e2278c37 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -342,8 +342,8 @@ namespace eosiosystem { // - `version` defaulted to zero, // - `last_dist_time` the last time proceeds from renting, ram fees, and name bids were added to the rex pool, // - `pending_bucket_time` timestamp of the pending 12-hour return bucket, - // - `oldest_bucket_time` cached timestamp of the oldest 12-hour return bucket, - // - `pending_bucket_proceeds` proceeds in the pending 12-hour return bucket, + // - `oldest_bucket_time` cached timestamp of the oldest 12-hour return bucket, + // - `pending_bucket_proceeds` proceeds in the pending 12-hour return bucket, // - `current_rate_of_increase` the current rate per dist_interval at which proceeds are added to the rex pool, // - `proceeds` the maximum amount of proceeds that can be added to the rex pool at any given time struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_pool { @@ -367,7 +367,7 @@ namespace eosiosystem { // `rex_return_buckets` structure underlying the rex return buckets table. A rex return buckets table is defined by: // - `version` defaulted to zero, - // - `return_buckets` buckets of proceeds accumulated in 12-hour intervals + // - `return_buckets` buckets of proceeds accumulated in 12-hour intervals struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_buckets { uint8_t version = 0; std::map return_buckets; @@ -518,8 +518,8 @@ namespace eosiosystem { time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this // time hits, weight_ratio will be target_weight_ratio. double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs = 0; // Number of seconds for adjusted resource utilization to - // decay to instantaneous utilization within exp(-1). + uint32_t decay_secs; = 0; // Number of seconds for the gap between adjusted resource + // utilization and instantaneous utilization to shrink by 63%. asset target_price = {}; // Fee needed to rent the entire resource market weight. int64_t utilization = 0; // Instantaneous resource utilization. This is the current // amount sold. utilization <= weight. From a5cdbc29c2f4f9239c36f1a9a69e027853edf129 Mon Sep 17 00:00:00 2001 From: arhag Date: Mon, 16 Dec 2019 22:18:54 -0500 Subject: [PATCH 29/45] fix typo in previous commit --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 6e2278c37..8a94be787 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -518,7 +518,7 @@ namespace eosiosystem { time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this // time hits, weight_ratio will be target_weight_ratio. double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs; = 0; // Number of seconds for the gap between adjusted resource + uint32_t decay_secs = 0; // Number of seconds for the gap between adjusted resource // utilization and instantaneous utilization to shrink by 63%. asset target_price = {}; // Fee needed to rent the entire resource market weight. int64_t utilization = 0; // Instantaneous resource utilization. This is the current From 74b22ed296276f5f542fb13a9f368ece00177908 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 14:46:12 -0500 Subject: [PATCH 30/45] replace calc_rentbw_price with calc_rentbw_fee --- contracts/eosio.system/src/rentbw.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 4b2af9db1..f75118af8 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -210,8 +210,11 @@ void system_contract::configrentbw(rentbw_config& args) { state_sing.set(state, get_self()); } // system_contract::configrentbw -int64_t calc_rentbw_price(const rentbw_state_resource& state, double utilization) { - return std::ceil(state.target_price.amount * std::pow(utilization / state.weight, state.exponent)); +int64_t calc_rentbw_fee(const rentbw_state_resource& state, int64_t utilization_increase) { + double start_utilization = state.adjusted_utilization; + double end_utilization = state.adjusted_utilization + utilization_increase; + return std::ceil(state.target_price.amount * std::pow(end_utilization / state.weight, state.exponent)) + - std::ceil(state.target_price.amount * std::pow(start_utilization / state.weight, state.exponent)); } void system_contract::rentbwexec(const name& user, uint16_t max) { @@ -258,8 +261,7 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d amount = int128_t(frac) * state.weight / rentbw_frac; eosio::check(state.weight, "market doesn't have resources available"); eosio::check(state.utilization + amount <= state.weight, "market doesn't have enough resources available"); - int64_t f = calc_rentbw_price(state, state.adjusted_utilization + amount) - - calc_rentbw_price(state, state.adjusted_utilization); + int64_t f = calc_rentbw_fee(state, amount); eosio::check(f > 0, "calculated fee is below minimum; try renting more"); fee.amount += f; state.utilization += amount; From 4bb6b3dc795a636169b30de31c3a4de8ed70bca9 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 14:51:29 -0500 Subject: [PATCH 31/45] prefer rounding in a way that ensures fee calculation does not give advantage to very small rentals under particular situations (but still under the condition of utilization >= adjusted_utilization) Also add calculated fee to the error message when it exceeds max_payment. --- contracts/eosio.system/src/rentbw.cpp | 13 ++++++++++--- tests/eosio.rentbw_tests.cpp | 14 +++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index f75118af8..cb94c98de 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -213,8 +213,11 @@ void system_contract::configrentbw(rentbw_config& args) { int64_t calc_rentbw_fee(const rentbw_state_resource& state, int64_t utilization_increase) { double start_utilization = state.adjusted_utilization; double end_utilization = state.adjusted_utilization + utilization_increase; - return std::ceil(state.target_price.amount * std::pow(end_utilization / state.weight, state.exponent)) - - std::ceil(state.target_price.amount * std::pow(start_utilization / state.weight, state.exponent)); + + double fee = state.target_price.amount * std::pow(end_utilization / state.weight, state.exponent) - + state.target_price.amount * std::pow(start_utilization / state.weight, state.exponent); + + return std::ceil(fee); } void system_contract::rentbwexec(const name& user, uint16_t max) { @@ -271,7 +274,11 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d int64_t cpu_amount = 0; process(net_frac, net_amount, state.net); process(cpu_frac, cpu_amount, state.cpu); - eosio::check(fee <= max_payment, "calculated fee exceeds max_payment"); + if (fee > max_payment) { + std::string error_msg = "max_payment is less than calculated fee: "; + error_msg += fee.to_string(); + eosio::check(false, error_msg); + } eosio::check(fee >= state.min_rent_price, "calculated fee is below minimum; try renting more"); orders.emplace(payer, [&](auto& order) { diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 4306ce383..b05120a98 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -515,7 +515,7 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac + 1, asset::from_string("1.0000 TST"))); BOOST_REQUIRE_EQUAL( - t.wasm_assert_msg("calculated fee exceeds max_payment"), // + t.wasm_assert_msg("max_payment is less than calculated fee: 3000000.0000 TST"), // t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, asset::from_string("1.0000 TST"))); BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("can't channel fees to rex"), // t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, rentbw_frac, @@ -573,9 +573,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // (.35 ^ 2) * 1000000.0000 - 90000.0000 = 32500.0000 // (.5 ^ 3) * 2000000.0000 - 128000.0000 = 122000.0000 // total = 154500.0000 - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("154499.9999")); + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("154500.0000")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .05, rentbw_frac * .10, - asset::from_string("154499.9999 TST"), net_weight * .05, cpu_weight * .10); + asset::from_string("154500.0000 TST"), net_weight * .05, cpu_weight * .10); } { @@ -650,9 +650,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // [((e^-2 + 1.0) ^ 2) - ((e^-2) ^ 2) ] * 1000000.0000 = 1270670.5664 // [((e^-2 + 1.0) ^ 3) - ((e^-2) ^ 3) ] * 2000000.0000 = 2921905.5327 // total = 4192576.0991 - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("4192561.0244")); + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("4192561.0246")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, - asset::from_string("4192561.0244 TST"), net_weight, cpu_weight); + asset::from_string("4192561.0246 TST"), net_weight, cpu_weight); } { @@ -673,9 +673,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // (.3 ^ 2) * 1000000.0000 - 10000.0000 = 80000.0000 // (.4 ^ 3) * 2000000.0000 - 16000.0000 = 112000.0000 // total = 192000.0000 - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("191999.9999")); + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("192000.0001")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .2, rentbw_frac * .2, - asset::from_string("191999.9999 TST"), net_weight * .2, cpu_weight * .2); + asset::from_string("192000.0001 TST"), net_weight * .2, cpu_weight * .2); // Start decay t.produce_block(fc::days(15) - fc::milliseconds(1000)); From 2846b1030e7d14b96b3c32ab548b078526c14d86 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 16:24:37 -0500 Subject: [PATCH 32/45] fix name of configrentbw action wrapper type --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 8a94be787..591fdbab2 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -1270,7 +1270,7 @@ namespace eosiosystem { using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; - using configcpu_action = eosio::action_wrapper<"configrentbw"_n, &system_contract::configrentbw>; + using configrentbw_action = eosio::action_wrapper<"configrentbw"_n, &system_contract::configrentbw>; using rentbwexec_action = eosio::action_wrapper<"rentbwexec"_n, &system_contract::rentbwexec>; using rentbw_action = eosio::action_wrapper<"rentbw"_n, &system_contract::rentbw>; From 6375355a1ace2f0f403491623ed77f09a2c9a701 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 17:35:20 -0500 Subject: [PATCH 33/45] add default values for rent_days, decay_secs, and exponent --- .../include/eosio.system/eosio.system.hpp | 64 +++++++++++-------- tests/eosio.rentbw_tests.cpp | 12 ++-- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 591fdbab2..4984d3db1 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -38,7 +38,7 @@ namespace eosiosystem { using eosio::time_point_sec; using eosio::unsigned_int; - inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 + inline constexpr int64_t rentbw_frac = 1'000'000'000'000'000ll; // 1.0 = 10^15 template static inline auto has_field( F flags, E field ) @@ -504,36 +504,48 @@ namespace eosiosystem { }; struct rentbw_state_resource { + static constexpr double default_exponent = 2.0; // Exponent of 2.0 means that the price to rent a + // tiny amount of resources increases linearly + // with utilization. + static constexpr uint32_t default_decay_secs = 1 * seconds_per_day; // 1 day; if 100% of bandwidth resources are in a + // single loan, then, assuming no further renting, + // 1 day after it expires the adjusted utilization + // will be at approximately 37% and after 3 days the + // adjusted utilization will be at least than 5%. + + uint8_t version = 0; - int64_t weight = 0; // resource market weight. calculated; varies over time. - // 1 represents the same amount of resources as 1 - // satoshi of SYS staked. - int64_t weight_ratio = 0; // resource market weight ratio: - // assumed_stake_weight / (assumed_stake_weight + weight). - // calculated; varies over time. 1x = 10^15. 0.01x = 10^13. - int64_t assumed_stake_weight = 0; // Assumed stake weight for ratio calculations. - int64_t initial_weight_ratio = rentbw_frac; // Initial weight_ratio used for linear shrinkage. - int64_t target_weight_ratio = rentbw_frac / 100; // Linearly shrink the weight_ratio to this amount. - time_point_sec initial_timestamp = {}; // When weight_ratio shrinkage started - time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this - // time hits, weight_ratio will be target_weight_ratio. - double exponent = 0; // Exponent of resource price curve. - uint32_t decay_secs = 0; // Number of seconds for the gap between adjusted resource - // utilization and instantaneous utilization to shrink by 63%. - asset target_price = {}; // Fee needed to rent the entire resource market weight. - int64_t utilization = 0; // Instantaneous resource utilization. This is the current - // amount sold. utilization <= weight. - int64_t adjusted_utilization = 0; // Adjusted resource utilization. This is >= utilization and - // <= weight. It grows instantly but decays exponentially. - time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated + int64_t weight = 0; // resource market weight. calculated; varies over time. + // 1 represents the same amount of resources as 1 + // satoshi of SYS staked. + int64_t weight_ratio = 0; // resource market weight ratio: + // assumed_stake_weight / (assumed_stake_weight + weight). + // calculated; varies over time. 1x = 10^15. 0.01x = 10^13. + int64_t assumed_stake_weight = 0; // Assumed stake weight for ratio calculations. + int64_t initial_weight_ratio = rentbw_frac; // Initial weight_ratio used for linear shrinkage. + int64_t target_weight_ratio = rentbw_frac / 100; // Linearly shrink the weight_ratio to this amount. + time_point_sec initial_timestamp = {}; // When weight_ratio shrinkage started + time_point_sec target_timestamp = {}; // Stop automatic weight_ratio shrinkage at this time. Once this + // time hits, weight_ratio will be target_weight_ratio. + double exponent = default_exponent; // Exponent of resource price curve. + uint32_t decay_secs = default_decay_secs; // Number of seconds for the gap between adjusted resource + // utilization and instantaneous utilization to shrink by 63%. + asset target_price = {}; // Fee needed to rent the entire resource market weight. + int64_t utilization = 0; // Instantaneous resource utilization. This is the current + // amount sold. utilization <= weight. + int64_t adjusted_utilization = 0; // Adjusted resource utilization. This is >= utilization and + // <= weight. It grows instantly but decays exponentially. + time_point_sec utilization_timestamp = {}; // When adjusted_utilization was last updated }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { + static constexpr uint32_t default_rent_days = 30 * seconds_per_day; // 30 day resource rentals + uint8_t version = 0; - rentbw_state_resource net = {}; // NET market state - rentbw_state_resource cpu = {}; // CPU market state - uint32_t rent_days = 0; // `rentbw` `days` argument must match this. - asset min_rent_price = {}; // Rents below this amount are rejected + rentbw_state_resource net = {}; // NET market state + rentbw_state_resource cpu = {}; // CPU market state + uint32_t rent_days = default_rent_days; // `rentbw` `days` argument must match this. + asset min_rent_price = {}; // Rents below this amount are rejected uint64_t primary_key()const { return 0; } }; diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index b05120a98..d04c8102c 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -226,8 +226,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { push_action(N(alice1111111), N(configrentbw), mvo()("args", make_config()))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("rentbw hasn't been initialized"), rentbwexec(N(alice1111111), 10)); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), - configbw(make_config([&](auto& c) { c.rent_days = 0; }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), + // configbw(make_config([&](auto& c) { c.rent_days = 0; }))); // needed only if rent_days does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("1000000.000 TST"); }))); @@ -255,8 +255,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), configbw(make_config([&](auto& c) { c.net.exponent = .999; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), - configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + // configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); // needed only if decay_secs does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("1000000.000 TST"); }))); @@ -284,8 +284,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), configbw(make_config([&](auto& c) { c.cpu.exponent = .999; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), - configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + // configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); // needed only if decay_secs does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("1000000.000 TST"); }))); From 46214fbc7db69eef6d33ded77c8b7437fd5f2c55 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 18:47:03 -0500 Subject: [PATCH 34/45] add default values for min_rent_price and target_price as well --- .../include/eosio.system/eosio.system.hpp | 23 +++++++++++-------- contracts/eosio.system/src/rentbw.cpp | 16 +++++++++---- tests/eosio.rentbw_tests.cpp | 12 +++++----- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 4984d3db1..536ebfd82 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -504,15 +504,16 @@ namespace eosiosystem { }; struct rentbw_state_resource { - static constexpr double default_exponent = 2.0; // Exponent of 2.0 means that the price to rent a - // tiny amount of resources increases linearly - // with utilization. - static constexpr uint32_t default_decay_secs = 1 * seconds_per_day; // 1 day; if 100% of bandwidth resources are in a - // single loan, then, assuming no further renting, - // 1 day after it expires the adjusted utilization - // will be at approximately 37% and after 3 days the - // adjusted utilization will be at least than 5%. - + static constexpr double default_exponent = 2.0; // Exponent of 2.0 means that the price to rent a + // tiny amount of resources increases linearly + // with utilization. + static constexpr uint32_t default_decay_secs = 1 * seconds_per_day; // 1 day; if 100% of bandwidth resources are in a + // single loan, then, assuming no further renting, + // 1 day after it expires the adjusted utilization + // will be at approximately 37% and after 3 days the + // adjusted utilization will be at least than 5%. + static constexpr int64_t default_target_price = 100'000'000'0000ll; // 100000000.0000 SYS + // (assuming get_core_symbol() == symbol("SYS", 4)); uint8_t version = 0; int64_t weight = 0; // resource market weight. calculated; varies over time. @@ -539,7 +540,9 @@ namespace eosiosystem { }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { - static constexpr uint32_t default_rent_days = 30 * seconds_per_day; // 30 day resource rentals + static constexpr uint32_t default_rent_days = 30 * seconds_per_day; // 30 day resource rentals + static constexpr int64_t default_min_rent_price = 100ll; // 0.0100 SYS + // (assuming get_core_symbol() == symbol("SYS", 4)) uint8_t version = 0; rentbw_state_resource net = {}; // NET market state diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index cb94c98de..02d70fc11 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -153,8 +153,12 @@ void system_contract::configrentbw(rentbw_config& args) { args.exponent = state.exponent; if (!args.decay_secs) args.decay_secs = state.decay_secs; - if (!args.target_price.amount && state.target_price.amount) - args.target_price = state.target_price; + if (!args.target_price.amount) { + if (state.target_price.amount) + args.target_price = state.target_price; + else + args.target_price.amount = rentbw_state_resource::default_target_price; + } if (args.current_weight_ratio == args.target_weight_ratio) args.target_timestamp = now; @@ -186,8 +190,12 @@ void system_contract::configrentbw(rentbw_config& args) { if (!args.rent_days) args.rent_days = state.rent_days; - if (!args.min_rent_price.amount && state.min_rent_price.amount) - args.min_rent_price = state.min_rent_price; + if (!args.min_rent_price.amount) { + if (state.min_rent_price.amount) + args.min_rent_price = state.min_rent_price; + else + args.min_rent_price.amount = rentbw_state::default_min_rent_price; + } eosio::check(args.rent_days > 0, "rent_days must be > 0"); eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index d04c8102c..8d982b4e3 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -231,8 +231,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("1000000.000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), - configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), + // configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); // needed only if min_rent_price does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("-1.0000 TST"); }))); @@ -260,8 +260,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("1000000.000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + // configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); // needed only if target_price does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("-1.0000 TST"); }))); @@ -289,8 +289,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("1000000.000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); + //BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + // configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); // needed only if target_price does not have default BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); } // config_tests From 5bb3bb5ede900cda10e6f777f42b6fd84bc4b4ae Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 18:52:33 -0500 Subject: [PATCH 35/45] fix wrong default value for rent_days --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 536ebfd82..136dc9394 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -540,9 +540,8 @@ namespace eosiosystem { }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { - static constexpr uint32_t default_rent_days = 30 * seconds_per_day; // 30 day resource rentals - static constexpr int64_t default_min_rent_price = 100ll; // 0.0100 SYS - // (assuming get_core_symbol() == symbol("SYS", 4)) + static constexpr uint32_t default_rent_days = 30; // 30 day resource rentals + static constexpr int64_t default_min_rent_price = 100ll; // 0.0100 SYS (assuming get_core_symbol() == symbol("SYS", 4)) uint8_t version = 0; rentbw_state_resource net = {}; // NET market state From 4c9f791c593e83b5d9a7e9999bbe76db7b75878a Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 19:46:33 -0500 Subject: [PATCH 36/45] fix comment --- .../eosio.system/include/eosio.system/eosio.system.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 136dc9394..c2180e65e 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -510,10 +510,10 @@ namespace eosiosystem { static constexpr uint32_t default_decay_secs = 1 * seconds_per_day; // 1 day; if 100% of bandwidth resources are in a // single loan, then, assuming no further renting, // 1 day after it expires the adjusted utilization - // will be at approximately 37% and after 3 days the - // adjusted utilization will be at least than 5%. + // will be at approximately 37% and after 3 days + // the adjusted utilization will be less than 5%. static constexpr int64_t default_target_price = 100'000'000'0000ll; // 100000000.0000 SYS - // (assuming get_core_symbol() == symbol("SYS", 4)); + // (assuming get_core_symbol() == symbol("SYS", 4)) uint8_t version = 0; int64_t weight = 0; // resource market weight. calculated; varies over time. From be4a296d70fb5f50d18501e04011daf27849666c Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 19:58:01 -0500 Subject: [PATCH 37/45] fix comment description of some configuration options --- .../include/eosio.system/eosio.system.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index c2180e65e..867378f7a 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -486,21 +486,21 @@ namespace eosiosystem { // current_weight_ratio == target_weight_ratio. Set this to 0 to preserve the // existing setting. double exponent; // Exponent of resource price curve. Must be >= 1. Set this to 0 to preserve the - // existing setting. + // existing setting or use the default. uint32_t decay_secs; // Number of seconds for the gap between adjusted resource utilization and // instantaneous utilization to shrink by 63%. Set this to 0 to preserve the - // existing setting. - asset target_price; // Fee needed to rent the entire resource market weight. Set this to 0 to - // preserve the existing setting. + // existing setting or use the default. + asset target_price; // Fee needed to rent the entire resource market weight. Set the amount of this + // asset to 0 to preserve the existing setting or use the default. }; struct rentbw_config { rentbw_config_resource net; // NET market configuration rentbw_config_resource cpu; // CPU market configuration uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the - // existing setting. - asset min_rent_price; // Rents below this amount are rejected. Set this to 0 to preserve the - // existing setting. + // existing setting or use the default. + asset min_rent_price; // Rents below this amount are rejected. Set the amount of this asset to 0 to + // preserve the existing setting or use the default. }; struct rentbw_state_resource { From f85efb42c3c12c846fe15ee78d9b589d98fb4225 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 18 Dec 2019 20:31:09 -0500 Subject: [PATCH 38/45] remove defaults for min_rent_price and target_price since values that are resonable dependent heavily on the attributes of the core token --- .../include/eosio.system/eosio.system.hpp | 7 ++----- contracts/eosio.system/src/rentbw.cpp | 16 ++++------------ tests/eosio.rentbw_tests.cpp | 14 +++++++------- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 867378f7a..9f2f47682 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -491,7 +491,7 @@ namespace eosiosystem { // instantaneous utilization to shrink by 63%. Set this to 0 to preserve the // existing setting or use the default. asset target_price; // Fee needed to rent the entire resource market weight. Set the amount of this - // asset to 0 to preserve the existing setting or use the default. + // asset to 0 to preserve the existing setting. }; struct rentbw_config { @@ -500,7 +500,7 @@ namespace eosiosystem { uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the // existing setting or use the default. asset min_rent_price; // Rents below this amount are rejected. Set the amount of this asset to 0 to - // preserve the existing setting or use the default. + // preserve the existing setting. }; struct rentbw_state_resource { @@ -512,8 +512,6 @@ namespace eosiosystem { // 1 day after it expires the adjusted utilization // will be at approximately 37% and after 3 days // the adjusted utilization will be less than 5%. - static constexpr int64_t default_target_price = 100'000'000'0000ll; // 100000000.0000 SYS - // (assuming get_core_symbol() == symbol("SYS", 4)) uint8_t version = 0; int64_t weight = 0; // resource market weight. calculated; varies over time. @@ -541,7 +539,6 @@ namespace eosiosystem { struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { static constexpr uint32_t default_rent_days = 30; // 30 day resource rentals - static constexpr int64_t default_min_rent_price = 100ll; // 0.0100 SYS (assuming get_core_symbol() == symbol("SYS", 4)) uint8_t version = 0; rentbw_state_resource net = {}; // NET market state diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 02d70fc11..cb94c98de 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -153,12 +153,8 @@ void system_contract::configrentbw(rentbw_config& args) { args.exponent = state.exponent; if (!args.decay_secs) args.decay_secs = state.decay_secs; - if (!args.target_price.amount) { - if (state.target_price.amount) - args.target_price = state.target_price; - else - args.target_price.amount = rentbw_state_resource::default_target_price; - } + if (!args.target_price.amount && state.target_price.amount) + args.target_price = state.target_price; if (args.current_weight_ratio == args.target_weight_ratio) args.target_timestamp = now; @@ -190,12 +186,8 @@ void system_contract::configrentbw(rentbw_config& args) { if (!args.rent_days) args.rent_days = state.rent_days; - if (!args.min_rent_price.amount) { - if (state.min_rent_price.amount) - args.min_rent_price = state.min_rent_price; - else - args.min_rent_price.amount = rentbw_state::default_min_rent_price; - } + if (!args.min_rent_price.amount && state.min_rent_price.amount) + args.min_rent_price = state.min_rent_price; eosio::check(args.rent_days > 0, "rent_days must be > 0"); eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 8d982b4e3..9ca41b564 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -12,7 +12,7 @@ #include "eosio.system_tester.hpp" -inline constexpr int64_t rentbw_frac = 1000000000000000ll; // 1.0 = 10^15 +inline constexpr int64_t rentbw_frac = 1'000'000'000'000'000ll; // 1.0 = 10^15 inline constexpr int64_t stake_weight = 100'000'000'0000ll; // 10^12 struct rentbw_config_resource { @@ -231,8 +231,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("1000000.000 TST"); }))); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), - // configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); // needed only if min_rent_price does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), + configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("-1.0000 TST"); }))); @@ -260,8 +260,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("1000000.000 TST"); }))); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - // configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); // needed only if target_price does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("-1.0000 TST"); }))); @@ -289,8 +289,8 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("1000000.000 TST"); }))); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - // configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); // needed only if target_price does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), + configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); } // config_tests From d1bc1732b1a2d3c3634e6fb7c6e872a858b428db Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 19 Dec 2019 13:29:51 -0500 Subject: [PATCH 39/45] modify fee calculation to not give advantage to very small rentals even under the scenario of utilization <= adjusted_utilization --- contracts/eosio.system/src/rentbw.cpp | 49 ++++++++++++++++++++++++--- tests/eosio.rentbw_tests.cpp | 20 +++++------ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index cb94c98de..cacd5e53c 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -210,12 +210,53 @@ void system_contract::configrentbw(rentbw_config& args) { state_sing.set(state, get_self()); } // system_contract::configrentbw +/** + * @pre 0 < state.target_price.amount + * @pre 1.0 <= state.exponent + * @pre 0 <= state.utilization <= state.adjusted_utilization <= state.weight + * @pre 0 <= utilization_increase <= (state.weight - state.utilization) + */ int64_t calc_rentbw_fee(const rentbw_state_resource& state, int64_t utilization_increase) { - double start_utilization = state.adjusted_utilization; - double end_utilization = state.adjusted_utilization + utilization_increase; + if( utilization_increase <= 0 ) return 0; + + // Let p(u) = price as a function of the utilization fraction u which is defined for u in [0.0, 1.0]. + // Let f(u) = integral of the price function p(x) from x = 0.0 to x = u, again defined for u in [0.0, 1.0]. + + // Returns f(double(end_utilization)/state.weight) - f(double(start_utilization)/state.weight) which is equivalent to + // the integral of p(x) from x = double(start_utilization)/state.weight to x = double(end_utilization)/state.weight. + // @pre 0 <= start_utilization <= end_utilization <= state.weight + auto price_integral_delta = [&state](int64_t start_utilization, int64_t end_utilization) -> double { + return state.target_price.amount * std::pow(double(end_utilization) / state.weight, state.exponent) - + state.target_price.amount * std::pow(double(start_utilization) / state.weight, state.exponent); + }; + + // Returns p(double(utilization)/state.weight). + // @pre 0 <= utilization <= state.weight + auto price_function = [&state](int64_t utilization) -> double { + // state.exponent >= 1.0, therefore the exponent passed into std::pow is >= 0.0. + // Since the exponent passed into std::pow could be 0.0 and simultaneously so could double(utilization)/state.weight, + // the safest thing to do is handle that as a special case explicitly rather than relying on std::pow to return 1.0 + // instead of triggering a domain error. + double new_exponent = state.exponent - 1.0; + if (new_exponent <= 0.0) + return state.target_price.amount; + else + return state.exponent * state.target_price.amount * std::pow(double(utilization) / state.weight, new_exponent); + }; + + double fee = 0.0; + int64_t start_utilization = state.utilization; + int64_t end_utilization = start_utilization + utilization_increase; - double fee = state.target_price.amount * std::pow(end_utilization / state.weight, state.exponent) - - state.target_price.amount * std::pow(start_utilization / state.weight, state.exponent); + if (start_utilization < state.adjusted_utilization) { + fee += price_function(state.adjusted_utilization) * + std::min(utilization_increase, state.adjusted_utilization - start_utilization) / state.weight; + start_utilization = state.adjusted_utilization; + } + + if (start_utilization < end_utilization) { + fee += price_integral_delta(start_utilization, end_utilization); + } return std::ceil(fee); } diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 9ca41b564..23f3fb9a9 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -602,12 +602,12 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // immediate renewal: adjusted_utilization doesn't have time to fall // - // (2.0 ^ 2) * 1000000.0000 - 1000000.0000 = 3000000.0000 - // (2.0 ^ 3) * 2000000.0000 - 2000000.0000 = 14000000.0000 - // total = 17000000.0000 - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("17000000.0000")); + // 2 * (1.0 ^ 1) * 1000000.0000 = 2000000.0000 + // 3 * (1.0 ^ 2) * 2000000.0000 = 6000000.0000 + // total = 8000000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("8000000.0000")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, - asset::from_string("17000000.0000 TST"), 0, 0); + asset::from_string("8000000.0000 TST"), 0, 0); // No more available for 30 days BOOST_REQUIRE_EQUAL(t.wasm_assert_msg("market doesn't have enough resources available"), // @@ -647,12 +647,12 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // 100% after 2 days of decay // - // [((e^-2 + 1.0) ^ 2) - ((e^-2) ^ 2) ] * 1000000.0000 = 1270670.5664 - // [((e^-2 + 1.0) ^ 3) - ((e^-2) ^ 3) ] * 2000000.0000 = 2921905.5327 - // total = 4192576.0991 - t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("4192561.0246")); + // [ [2 * ((e^-2) ^ 1)]*(e^-2 - 0.0) + ((1.0) ^ 2) - ((e^-2) ^ 2) ] * 1000000.0000 = 1018315.6389 + // [ [3 * ((e^-2) ^ 2)]*(e^-2 - 0.0) + ((1.0) ^ 3) - ((e^-2) ^ 3) ] * 2000000.0000 = 2009915.0087 + // total = 3028230.6476 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3028229.8795")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, - asset::from_string("4192561.0246 TST"), net_weight, cpu_weight); + asset::from_string("3028229.8795 TST"), net_weight, cpu_weight); } { From b1d471413ed40b9bc4275339dde64d509c49e39d Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 19 Dec 2019 13:37:21 -0500 Subject: [PATCH 40/45] rename min_rent_price to min_rent_fee --- .../include/eosio.system/eosio.system.hpp | 24 ++++++------ contracts/eosio.system/src/rentbw.cpp | 14 +++---- tests/eosio.rentbw_tests.cpp | 38 +++++++++---------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 9f2f47682..f0b579462 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -495,12 +495,12 @@ namespace eosiosystem { }; struct rentbw_config { - rentbw_config_resource net; // NET market configuration - rentbw_config_resource cpu; // CPU market configuration - uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the - // existing setting or use the default. - asset min_rent_price; // Rents below this amount are rejected. Set the amount of this asset to 0 to - // preserve the existing setting. + rentbw_config_resource net; // NET market configuration + rentbw_config_resource cpu; // CPU market configuration + uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the + // existing setting or use the default. + asset min_rent_fee; // Rental fees below this amount are rejected. Set the amount of this asset to 0 + // to preserve the existing setting. }; struct rentbw_state_resource { @@ -538,13 +538,13 @@ namespace eosiosystem { }; struct [[eosio::table("rent.state"),eosio::contract("eosio.system")]] rentbw_state { - static constexpr uint32_t default_rent_days = 30; // 30 day resource rentals + static constexpr uint32_t default_rent_days = 30; // 30 day resource rentals - uint8_t version = 0; - rentbw_state_resource net = {}; // NET market state - rentbw_state_resource cpu = {}; // CPU market state - uint32_t rent_days = default_rent_days; // `rentbw` `days` argument must match this. - asset min_rent_price = {}; // Rents below this amount are rejected + uint8_t version = 0; + rentbw_state_resource net = {}; // NET market state + rentbw_state_resource cpu = {}; // CPU market state + uint32_t rent_days = default_rent_days; // `rentbw` `days` argument must match this. + asset min_rent_fee = {}; // Rental fees below this amount are rejected uint64_t primary_key()const { return 0; } }; diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index cacd5e53c..6add2ee77 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -186,15 +186,15 @@ void system_contract::configrentbw(rentbw_config& args) { if (!args.rent_days) args.rent_days = state.rent_days; - if (!args.min_rent_price.amount && state.min_rent_price.amount) - args.min_rent_price = state.min_rent_price; + if (!args.min_rent_fee.amount && state.min_rent_fee.amount) + args.min_rent_fee = state.min_rent_fee; eosio::check(args.rent_days > 0, "rent_days must be > 0"); - eosio::check(args.min_rent_price.symbol == core_symbol, "min_rent_price doesn't match core symbol"); - eosio::check(args.min_rent_price.amount > 0, "min_rent_price must be positive"); + eosio::check(args.min_rent_fee.symbol == core_symbol, "min_rent_fee doesn't match core symbol"); + eosio::check(args.min_rent_fee.amount > 0, "min_rent_fee must be positive"); - state.rent_days = args.rent_days; - state.min_rent_price = args.min_rent_price; + state.rent_days = args.rent_days; + state.min_rent_fee = args.min_rent_fee; update(state.net, args.net); update(state.cpu, args.cpu); @@ -320,7 +320,7 @@ void system_contract::rentbw(const name& payer, const name& receiver, uint32_t d error_msg += fee.to_string(); eosio::check(false, error_msg); } - eosio::check(fee >= state.min_rent_price, "calculated fee is below minimum; try renting more"); + eosio::check(fee >= state.min_rent_fee, "calculated fee is below minimum; try renting more"); orders.emplace(payer, [&](auto& order) { order.id = orders.available_primary_key(); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 23f3fb9a9..d1d0f98cd 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -29,12 +29,12 @@ FC_REFLECT(rentbw_config_resource, (exponent)(decay_secs)(target_price)) struct rentbw_config { - rentbw_config_resource net = {}; - rentbw_config_resource cpu = {}; - uint32_t rent_days = {}; - asset min_rent_price = asset{}; + rentbw_config_resource net = {}; + rentbw_config_resource cpu = {}; + uint32_t rent_days = {}; + asset min_rent_fee = asset{}; }; -FC_REFLECT(rentbw_config, (net)(cpu)(rent_days)(min_rent_price)) +FC_REFLECT(rentbw_config, (net)(cpu)(rent_days)(min_rent_fee)) struct rentbw_state_resource { uint8_t version; @@ -62,9 +62,9 @@ struct rentbw_state { rentbw_state_resource net; rentbw_state_resource cpu; uint32_t rent_days; - asset min_rent_price; + asset min_rent_fee; }; -FC_REFLECT(rentbw_state, (version)(net)(cpu)(rent_days)(min_rent_price)) +FC_REFLECT(rentbw_state, (version)(net)(cpu)(rent_days)(min_rent_fee)) using namespace eosio_system; @@ -109,8 +109,8 @@ struct rentbw_tester : eosio_system_tester { config.cpu.decay_secs = fc::days(1).to_seconds(); config.cpu.target_price = asset::from_string("1000000.0000 TST"); - config.rent_days = 30; - config.min_rent_price = asset::from_string("1.0000 TST"); + config.rent_days = 30; + config.min_rent_fee = asset::from_string("1.0000 TST"); f(config); return config; @@ -228,13 +228,13 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { //BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), // configbw(make_config([&](auto& c) { c.rent_days = 0; }))); // needed only if rent_days does not have default - BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price doesn't match core symbol"), configbw(make_config([&](auto& c) { - c.min_rent_price = asset::from_string("1000000.000 TST"); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.min_rent_fee = asset::from_string("1000000.000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), - configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("0.0000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_price must be positive"), - configbw(make_config([&](auto& c) { c.min_rent_price = asset::from_string("-1.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee must be positive"), + configbw(make_config([&](auto& c) { c.min_rent_fee = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee must be positive"), + configbw(make_config([&](auto& c) { c.min_rent_fee = asset::from_string("-1.0000 TST"); }))); // net assertions BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), @@ -445,8 +445,8 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { config.cpu.exponent = 1; config.cpu.target_price = asset::from_string("1000000.0000 TST"); - config.rent_days = 30; - config.min_rent_price = asset::from_string("1.0000 TST"); + config.rent_days = 30; + config.min_rent_fee = asset::from_string("1.0000 TST"); }))); BOOST_REQUIRE_EQUAL( @@ -477,8 +477,8 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { config.cpu.exponent = 3; config.cpu.target_price = asset::from_string("2000000.0000 TST"); - config.rent_days = 30; - config.min_rent_price = asset::from_string("1.0000 TST"); + config.rent_days = 30; + config.min_rent_fee = asset::from_string("1.0000 TST"); }))); if (rex) From 0440491720ca6ab898904a19c760ee5fa74b4f6e Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 19 Dec 2019 17:25:54 -0500 Subject: [PATCH 41/45] use optionals rather than 0 for config parameters to signal that it should preserve the existing value (or use default when appropriate) --- .../include/eosio.system/eosio.system.hpp | 59 +++++---- contracts/eosio.system/src/rentbw.cpp | 121 +++++++++++------- tests/eosio.rentbw_tests.cpp | 74 ++++++++--- 3 files changed, 164 insertions(+), 90 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index f0b579462..7acbf4799 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -469,38 +469,45 @@ namespace eosiosystem { }; struct rentbw_config_resource { - int64_t current_weight_ratio; // Immediately set weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. Set this - // to 0 to preserve the existing setting or use the default; this avoids sudden - // price jumps. For new chains which don't need to gradually phase out staking - // and REX, 0.01x (10^13) is a good value for both current_weight_ratio and - // target_weight_ratio. - int64_t target_weight_ratio; // Linearly shrink weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. Set this - // to 0 to preserve the existing setting or use the default. - int64_t assumed_stake_weight; // Assumed stake weight for ratio calculations. Use the sum of total staked and - // total rented by REX at the time the rentbw market is first activated. Set - // this to 0 to preserve the existing setting; this avoids sudden price jumps. - // For new chains which don't need to phase out staking and REX, 10^12 is - // probably a good value. - time_point_sec target_timestamp; // Stop automatic weight_ratio shrinkage at this time. Once this - // time hits, weight_ratio will be target_weight_ratio. Ignored if - // current_weight_ratio == target_weight_ratio. Set this to 0 to preserve the - // existing setting. - double exponent; // Exponent of resource price curve. Must be >= 1. Set this to 0 to preserve the - // existing setting or use the default. - uint32_t decay_secs; // Number of seconds for the gap between adjusted resource utilization and - // instantaneous utilization to shrink by 63%. Set this to 0 to preserve the - // existing setting or use the default. - asset target_price; // Fee needed to rent the entire resource market weight. Set the amount of this - // asset to 0 to preserve the existing setting. + std::optional current_weight_ratio; // Immediately set weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. + // Do not specify to preserve the existing setting or use the default; + // this avoids sudden price jumps. For new chains which don't need + // to gradually phase out staking and REX, 0.01x (10^13) is a good + // value for both current_weight_ratio and target_weight_ratio. + std::optional target_weight_ratio; // Linearly shrink weight_ratio to this amount. 1x = 10^15. 0.01x = 10^13. + // Do not specify to preserve the existing setting or use the default. + std::optional assumed_stake_weight; // Assumed stake weight for ratio calculations. Use the sum of total + // staked and total rented by REX at the time the rentbw market + // is first activated. Do not specify to preserve the existing + // setting (no default exists); this avoids sudden price jumps. + // For new chains which don't need to phase out staking and REX, + // 10^12 is probably a good value. + std::optional target_timestamp; // Stop automatic weight_ratio shrinkage at this time. Once this + // time hits, weight_ratio will be target_weight_ratio. Ignored + // if current_weight_ratio == target_weight_ratio. Do not specify + // this to preserve the existing setting (no default exists). + std::optional exponent; // Exponent of resource price curve. Must be >= 1. Do not specify + // to preserve the existing setting or use the default. + std::optional decay_secs; // Number of seconds for the gap between adjusted resource + // utilization and instantaneous resource utilization to shrink + // by 63%. Do not specify to preserve the existing setting or + // use the default. + std::optional target_price; // Fee needed to rent the entire resource market weight. Do not + // specify to preserve the existing setting (no default exists). + + EOSLIB_SERIALIZE( rentbw_config_resource, (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight) + (target_timestamp)(exponent)(decay_secs)(target_price) ) }; struct rentbw_config { rentbw_config_resource net; // NET market configuration rentbw_config_resource cpu; // CPU market configuration - uint32_t rent_days; // `rentbw` `days` argument must match this. Set this to 0 to preserve the + std::optional rent_days; // `rentbw` `days` argument must match this. Do not specify to preserve the // existing setting or use the default. - asset min_rent_fee; // Rental fees below this amount are rejected. Set the amount of this asset to 0 - // to preserve the existing setting. + std::optional min_rent_fee; // Rental fees below this amount are rejected. Do not specify to preserve the + // existing setting (no default exists). + + EOSLIB_SERIALIZE( rentbw_config, (net)(cpu)(rent_days)(min_rent_fee) ) }; struct rentbw_state_resource { diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 6add2ee77..98513d96b 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -136,65 +136,90 @@ void system_contract::configrentbw(rentbw_config& args) { state.cpu.utilization_timestamp = now; } + auto is_default_asset = []( const eosio::asset& a ) -> bool { + return a.amount == 0 && a.symbol == symbol{}; + }; + auto update = [&](auto& state, auto& args) { if (!args.current_weight_ratio) { - if (state.weight_ratio) - args.current_weight_ratio = state.weight_ratio; - else - args.current_weight_ratio = state.initial_weight_ratio; + if (state.weight_ratio) { + *args.current_weight_ratio = state.weight_ratio; + } else { + *args.current_weight_ratio = state.initial_weight_ratio; + } } - if (!args.target_weight_ratio) - args.target_weight_ratio = state.target_weight_ratio; - if (!args.assumed_stake_weight) - args.assumed_stake_weight = state.assumed_stake_weight; - if (!args.target_timestamp.utc_seconds) - args.target_timestamp = state.target_timestamp; - if (!args.exponent) - args.exponent = state.exponent; - if (!args.decay_secs) - args.decay_secs = state.decay_secs; - if (!args.target_price.amount && state.target_price.amount) - args.target_price = state.target_price; - - if (args.current_weight_ratio == args.target_weight_ratio) - args.target_timestamp = now; - else - eosio::check(args.target_timestamp > now, "target_timestamp must be in the future"); - eosio::check(args.current_weight_ratio > 0, "current_weight_ratio is too small"); - eosio::check(args.current_weight_ratio <= rentbw_frac, "current_weight_ratio is too large"); - eosio::check(args.target_weight_ratio > 0, "target_weight_ratio is too small"); - eosio::check(args.target_weight_ratio <= args.current_weight_ratio, "weight can't grow over time"); - eosio::check(args.assumed_stake_weight >= 1, + + if (!args.target_weight_ratio) { + *args.target_weight_ratio = state.target_weight_ratio; + } + + if (!args.assumed_stake_weight) { + eosio::check(state.assumed_stake_weight != 0, "assumed_stake_weight does not have a default value"); + *args.assumed_stake_weight = state.assumed_stake_weight; + } + + if (*args.current_weight_ratio == *args.target_weight_ratio) { + *args.target_timestamp = now; + } else { + if (!args.target_timestamp) { + eosio::check(state.target_timestamp.utc_seconds != 0, "target_timestamp does not have a default value"); + *args.target_timestamp = state.target_timestamp; + } + eosio::check(*args.target_timestamp > now, "target_timestamp must be in the future"); + } + + if (!args.exponent) { + *args.exponent = state.exponent; + } + + if (!args.decay_secs) { + *args.decay_secs = state.decay_secs; + } + + if (!args.target_price) { + eosio::check(!is_default_asset(state.target_price), "target_price does not have a default value"); + *args.target_price = state.target_price; + } + + eosio::check(*args.current_weight_ratio > 0, "current_weight_ratio is too small"); + eosio::check(*args.current_weight_ratio <= rentbw_frac, "current_weight_ratio is too large"); + eosio::check(*args.target_weight_ratio > 0, "target_weight_ratio is too small"); + eosio::check(*args.target_weight_ratio <= *args.current_weight_ratio, "weight can't grow over time"); + eosio::check(*args.assumed_stake_weight >= 1, "assumed_stake_weight must be at least 1; a much larger value is recommended"); - eosio::check(args.assumed_stake_weight * int128_t(rentbw_frac) / args.target_weight_ratio <= + eosio::check(*args.assumed_stake_weight * int128_t(rentbw_frac) / *args.target_weight_ratio <= std::numeric_limits::max(), "assumed_stake_weight/target_weight_ratio is too large"); - eosio::check(args.exponent >= 1, "exponent must be >= 1"); - eosio::check(args.decay_secs >= 1, "decay_secs must be >= 1"); - eosio::check(args.target_price.symbol == core_symbol, "target_price doesn't match core symbol"); - eosio::check(args.target_price.amount > 0, "target_price must be positive"); - - state.assumed_stake_weight = args.assumed_stake_weight; - state.initial_weight_ratio = args.current_weight_ratio; - state.target_weight_ratio = args.target_weight_ratio; + eosio::check(*args.exponent >= 1, "exponent must be >= 1"); + eosio::check(*args.decay_secs >= 1, "decay_secs must be >= 1"); + eosio::check(args.target_price->symbol == core_symbol, "target_price doesn't match core symbol"); + eosio::check(args.target_price->amount > 0, "target_price must be positive"); + + state.assumed_stake_weight = *args.assumed_stake_weight; + state.initial_weight_ratio = *args.current_weight_ratio; + state.target_weight_ratio = *args.target_weight_ratio; state.initial_timestamp = now; - state.target_timestamp = args.target_timestamp; - state.exponent = args.exponent; - state.decay_secs = args.decay_secs; - state.target_price = args.target_price; + state.target_timestamp = *args.target_timestamp; + state.exponent = *args.exponent; + state.decay_secs = *args.decay_secs; + state.target_price = *args.target_price; }; - if (!args.rent_days) - args.rent_days = state.rent_days; - if (!args.min_rent_fee.amount && state.min_rent_fee.amount) - args.min_rent_fee = state.min_rent_fee; + if (!args.rent_days) { + *args.rent_days = state.rent_days; + } + + if (!args.min_rent_fee) { + eosio::check(!is_default_asset(state.min_rent_fee), "min_rent_fee does not have a default value"); + *args.min_rent_fee = state.min_rent_fee; + } - eosio::check(args.rent_days > 0, "rent_days must be > 0"); - eosio::check(args.min_rent_fee.symbol == core_symbol, "min_rent_fee doesn't match core symbol"); - eosio::check(args.min_rent_fee.amount > 0, "min_rent_fee must be positive"); + eosio::check(*args.rent_days > 0, "rent_days must be > 0"); + eosio::check(args.min_rent_fee->symbol == core_symbol, "min_rent_fee doesn't match core symbol"); + eosio::check(args.min_rent_fee->amount > 0, "min_rent_fee must be positive"); - state.rent_days = args.rent_days; - state.min_rent_fee = args.min_rent_fee; + state.rent_days = *args.rent_days; + state.min_rent_fee = *args.min_rent_fee; update(state.net, args.net); update(state.cpu, args.cpu); diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index d1d0f98cd..1bef88151 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -16,13 +16,13 @@ inline constexpr int64_t rentbw_frac = 1'000'000'000'000'000ll; // 1.0 = 10^15 inline constexpr int64_t stake_weight = 100'000'000'0000ll; // 10^12 struct rentbw_config_resource { - int64_t current_weight_ratio = {}; - int64_t target_weight_ratio = {}; - int64_t assumed_stake_weight = {}; - time_point_sec target_timestamp = {}; - double exponent = {}; - uint32_t decay_secs = {}; - asset target_price = asset{}; + fc::optional current_weight_ratio = {}; + fc::optional target_weight_ratio = {}; + fc::optional assumed_stake_weight = {}; + fc::optional target_timestamp = {}; + fc::optional exponent = {}; + fc::optional decay_secs = {}; + fc::optional target_price = {}; }; FC_REFLECT(rentbw_config_resource, // (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight)(target_timestamp) // @@ -31,8 +31,8 @@ FC_REFLECT(rentbw_config_resource, struct rentbw_config { rentbw_config_resource net = {}; rentbw_config_resource cpu = {}; - uint32_t rent_days = {}; - asset min_rent_fee = asset{}; + fc::optional rent_days = {}; + fc::optional min_rent_fee = {}; }; FC_REFLECT(rentbw_config, (net)(cpu)(rent_days)(min_rent_fee)) @@ -128,7 +128,35 @@ struct rentbw_tester : eosio_system_tester { } action_result configbw(const rentbw_config& config) { - return push_action(config::system_account_name, N(configrentbw), mvo()("args", config)); + // Verbose solution needed to work around bug in abi_serializer that fails if optional values aren't explicitly + // specified with a null value. + + auto optional_to_variant = []( const auto& v ) -> fc::variant { + return (!v ? fc::variant() : fc::variant(*v)); + }; + + auto resource_conf_vo = [&optional_to_variant](const rentbw_config_resource& c ) { + return mvo("current_weight_ratio", optional_to_variant(c.current_weight_ratio)) + ("target_weight_ratio", optional_to_variant(c.target_weight_ratio)) + ("assumed_stake_weight", optional_to_variant(c.assumed_stake_weight)) + ("target_timestamp", optional_to_variant(c.target_timestamp)) + ("exponent", optional_to_variant(c.exponent)) + ("decay_secs", optional_to_variant(c.decay_secs)) + ("target_price", optional_to_variant(c.target_price)) + ; + }; + + auto conf = mvo("net", resource_conf_vo(config.net)) + ("cpu", resource_conf_vo(config.cpu)) + ("rent_days", optional_to_variant(config.rent_days)) + ("min_rent_fee", optional_to_variant(config.min_rent_fee)) + ; + + //idump((fc::json::to_pretty_string(conf))); + return push_action(config::system_account_name, N(configrentbw), mvo()("args", std::move(conf))); + + // If abi_serializer worked correctly, the following is all that would be needed: + //return push_action(config::system_account_name, N(configrentbw), mvo()("args", config)); } action_result rentbwexec(name user, uint16_t max) { @@ -226,11 +254,13 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { push_action(N(alice1111111), N(configrentbw), mvo()("args", make_config()))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("rentbw hasn't been initialized"), rentbwexec(N(alice1111111), 10)); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), - // configbw(make_config([&](auto& c) { c.rent_days = 0; }))); // needed only if rent_days does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("rent_days must be > 0"), + configbw(make_config([&](auto& c) { c.rent_days = 0; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee doesn't match core symbol"), configbw(make_config([&](auto& c) { c.min_rent_fee = asset::from_string("1000000.000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee does not have a default value"), + configbw(make_config([&](auto& c) { c.min_rent_fee = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee must be positive"), configbw(make_config([&](auto& c) { c.min_rent_fee = asset::from_string("0.0000 TST"); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_rent_fee must be positive"), @@ -246,8 +276,12 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), configbw(make_config([](auto& c) { c.net.target_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight does not have a default value"), + configbw(make_config([](auto& c) { c.net.assumed_stake_weight = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), configbw(make_config([](auto& c) { c.net.assumed_stake_weight = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp does not have a default value"), + configbw(make_config([&](auto& c) { c.net.target_timestamp = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { c.net.target_timestamp = control->head_block_time(); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { @@ -255,8 +289,10 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), configbw(make_config([&](auto& c) { c.net.exponent = .999; }))); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), - // configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); // needed only if decay_secs does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price does not have a default value"), + configbw(make_config([&](auto& c) { c.net.target_price = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("1000000.000 TST"); }))); @@ -275,8 +311,12 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("weight can't grow over time"), configbw(make_config([](auto& c) { c.cpu.target_weight_ratio = rentbw_frac + 1; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight does not have a default value"), + configbw(make_config([](auto& c) { c.cpu.assumed_stake_weight = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("assumed_stake_weight must be at least 1; a much larger value is recommended"), configbw(make_config([](auto& c) { c.cpu.assumed_stake_weight = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp does not have a default value"), + configbw(make_config([&](auto& c) { c.cpu.target_timestamp = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { c.cpu.target_timestamp = control->head_block_time(); }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_timestamp must be in the future"), configbw(make_config([&](auto& c) { @@ -284,8 +324,10 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("exponent must be >= 1"), configbw(make_config([&](auto& c) { c.cpu.exponent = .999; }))); - //BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), - // configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); // needed only if decay_secs does not have default + BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), + configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price does not have a default value"), + configbw(make_config([&](auto& c) { c.cpu.target_price = {}; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("1000000.000 TST"); }))); From f23cbd4135220f6fe8b71a31bc351bfb3ea26537 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 19 Dec 2019 20:59:30 -0500 Subject: [PATCH 42/45] replace target_price with the more meaningful max_price; also change pricing model to now also include a min_price Note: Assuming min_price is 0, this new price model can be directly compared to the prior one. In fact, under that condition, max_price can be thought of as target_price * exponent. --- .../include/eosio.system/eosio.system.hpp | 15 +- contracts/eosio.system/src/rentbw.cpp | 56 +++-- tests/eosio.rentbw_tests.cpp | 201 ++++++++++++++---- 3 files changed, 208 insertions(+), 64 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 7acbf4799..f20b34ad6 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -492,11 +492,15 @@ namespace eosiosystem { // utilization and instantaneous resource utilization to shrink // by 63%. Do not specify to preserve the existing setting or // use the default. - std::optional target_price; // Fee needed to rent the entire resource market weight. Do not - // specify to preserve the existing setting (no default exists). + std::optional min_price; // Fee needed to rent the entire resource market weight at the + // minimum price. Do not specify to preserve the existing + // setting or use the default. + std::optional max_price; // Fee needed to rent the entire resource market weight at the + // maximum price. Do not specify to preserve the existing + // setting (no default exists). EOSLIB_SERIALIZE( rentbw_config_resource, (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight) - (target_timestamp)(exponent)(decay_secs)(target_price) ) + (target_timestamp)(exponent)(decay_secs)(min_price)(max_price) ) }; struct rentbw_config { @@ -536,7 +540,10 @@ namespace eosiosystem { double exponent = default_exponent; // Exponent of resource price curve. uint32_t decay_secs = default_decay_secs; // Number of seconds for the gap between adjusted resource // utilization and instantaneous utilization to shrink by 63%. - asset target_price = {}; // Fee needed to rent the entire resource market weight. + asset min_price = {}; // Fee needed to rent the entire resource market weight at + // the minimum price (defaults to 0). + asset max_price = {}; // Fee needed to rent the entire resource market weight at + // the maximum price. int64_t utilization = 0; // Instantaneous resource utilization. This is the current // amount sold. utilization <= weight. int64_t adjusted_utilization = 0; // Adjusted resource utilization. This is >= utilization and diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 98513d96b..6d5d385f3 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -176,9 +176,18 @@ void system_contract::configrentbw(rentbw_config& args) { *args.decay_secs = state.decay_secs; } - if (!args.target_price) { - eosio::check(!is_default_asset(state.target_price), "target_price does not have a default value"); - *args.target_price = state.target_price; + if (!args.max_price) { + eosio::check(!is_default_asset(state.max_price), "max_price does not have a default value"); + *args.max_price = state.max_price; + } + + if (!args.min_price) { + if (is_default_asset(state.min_price)) { + *args.min_price = *args.max_price; // just to copy symbol of max_price + args.min_price->amount = 0; // min_price has a default of zero. + } else { + *args.min_price = state.min_price; + } } eosio::check(*args.current_weight_ratio > 0, "current_weight_ratio is too small"); @@ -190,10 +199,16 @@ void system_contract::configrentbw(rentbw_config& args) { eosio::check(*args.assumed_stake_weight * int128_t(rentbw_frac) / *args.target_weight_ratio <= std::numeric_limits::max(), "assumed_stake_weight/target_weight_ratio is too large"); - eosio::check(*args.exponent >= 1, "exponent must be >= 1"); + eosio::check(*args.exponent >= 1.0, "exponent must be >= 1"); eosio::check(*args.decay_secs >= 1, "decay_secs must be >= 1"); - eosio::check(args.target_price->symbol == core_symbol, "target_price doesn't match core symbol"); - eosio::check(args.target_price->amount > 0, "target_price must be positive"); + eosio::check(args.max_price->symbol == core_symbol, "max_price doesn't match core symbol"); + eosio::check(args.max_price->amount > 0, "max_price must be positive"); + eosio::check(args.min_price->symbol == core_symbol, "min_price doesn't match core symbol"); + eosio::check(args.min_price->amount >= 0, "min_price must be non-negative"); + eosio::check(args.min_price->amount <= args.max_price->amount, "min_price cannot exceed max_price"); + if (*args.exponent == 1.0) { + eosio::check(args.min_price->amount == args.max_price->amount, "min_price and max_price must be the same if the exponent is 1"); + } state.assumed_stake_weight = *args.assumed_stake_weight; state.initial_weight_ratio = *args.current_weight_ratio; @@ -202,7 +217,8 @@ void system_contract::configrentbw(rentbw_config& args) { state.target_timestamp = *args.target_timestamp; state.exponent = *args.exponent; state.decay_secs = *args.decay_secs; - state.target_price = *args.target_price; + state.min_price = *args.min_price; + state.max_price = *args.max_price; }; if (!args.rent_days) { @@ -236,7 +252,9 @@ void system_contract::configrentbw(rentbw_config& args) { } // system_contract::configrentbw /** - * @pre 0 < state.target_price.amount + * @pre 0 == state.min_price.amount (for now) + * @pre 0 <= state.min_price.amount <= state.max_price.amount + * @pre 0 < state.max_price.amount * @pre 1.0 <= state.exponent * @pre 0 <= state.utilization <= state.adjusted_utilization <= state.weight * @pre 0 <= utilization_increase <= (state.weight - state.utilization) @@ -247,26 +265,36 @@ int64_t calc_rentbw_fee(const rentbw_state_resource& state, int64_t utilization_ // Let p(u) = price as a function of the utilization fraction u which is defined for u in [0.0, 1.0]. // Let f(u) = integral of the price function p(x) from x = 0.0 to x = u, again defined for u in [0.0, 1.0]. + // In particular we choose f(u) = min_price * u + ((max_price - min_price) / exponent) * (u ^ exponent). + // And so p(u) = min_price + (max_price - min_price) * (u ^ (exponent - 1.0)). + // Returns f(double(end_utilization)/state.weight) - f(double(start_utilization)/state.weight) which is equivalent to // the integral of p(x) from x = double(start_utilization)/state.weight to x = double(end_utilization)/state.weight. // @pre 0 <= start_utilization <= end_utilization <= state.weight auto price_integral_delta = [&state](int64_t start_utilization, int64_t end_utilization) -> double { - return state.target_price.amount * std::pow(double(end_utilization) / state.weight, state.exponent) - - state.target_price.amount * std::pow(double(start_utilization) / state.weight, state.exponent); + double coefficient = (state.max_price.amount - state.min_price.amount) / state.exponent; + double start_u = double(start_utilization) / state.weight; + double end_u = double(end_utilization) / state.weight; + return state.min_price.amount * end_u - state.min_price.amount * start_u + + coefficient * std::pow(end_u, state.exponent) - coefficient * std::pow(start_u, state.exponent); }; // Returns p(double(utilization)/state.weight). // @pre 0 <= utilization <= state.weight auto price_function = [&state](int64_t utilization) -> double { + double price = state.min_price.amount; // state.exponent >= 1.0, therefore the exponent passed into std::pow is >= 0.0. // Since the exponent passed into std::pow could be 0.0 and simultaneously so could double(utilization)/state.weight, // the safest thing to do is handle that as a special case explicitly rather than relying on std::pow to return 1.0 // instead of triggering a domain error. double new_exponent = state.exponent - 1.0; - if (new_exponent <= 0.0) - return state.target_price.amount; - else - return state.exponent * state.target_price.amount * std::pow(double(utilization) / state.weight, new_exponent); + if (new_exponent <= 0.0) { + return state.max_price.amount; + } else { + price += (state.max_price.amount - state.min_price.amount) * std::pow(double(utilization) / state.weight, new_exponent); + } + + return price; }; double fee = 0.0; diff --git a/tests/eosio.rentbw_tests.cpp b/tests/eosio.rentbw_tests.cpp index 1bef88151..115bfa838 100644 --- a/tests/eosio.rentbw_tests.cpp +++ b/tests/eosio.rentbw_tests.cpp @@ -22,11 +22,12 @@ struct rentbw_config_resource { fc::optional target_timestamp = {}; fc::optional exponent = {}; fc::optional decay_secs = {}; - fc::optional target_price = {}; + fc::optional min_price = {}; + fc::optional max_price = {}; }; FC_REFLECT(rentbw_config_resource, // (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight)(target_timestamp) // - (exponent)(decay_secs)(target_price)) + (exponent)(decay_secs)(min_price)(max_price)) struct rentbw_config { rentbw_config_resource net = {}; @@ -47,14 +48,15 @@ struct rentbw_state_resource { time_point_sec target_timestamp; double exponent; uint32_t decay_secs; - asset target_price; + asset min_price; + asset max_price; int64_t utilization; int64_t adjusted_utilization; time_point_sec utilization_timestamp; }; FC_REFLECT(rentbw_state_resource, // (version)(weight)(weight_ratio)(assumed_stake_weight)(initial_weight_ratio)(target_weight_ratio) // - (initial_timestamp)(target_timestamp)(exponent)(decay_secs)(target_price)(utilization) // + (initial_timestamp)(target_timestamp)(exponent)(decay_secs)(min_price)(max_price)(utilization) // (adjusted_utilization)(utilization_timestamp)) struct rentbw_state { @@ -99,7 +101,8 @@ struct rentbw_tester : eosio_system_tester { config.net.target_timestamp = control->head_block_time() + fc::days(100); config.net.exponent = 2; config.net.decay_secs = fc::days(1).to_seconds(); - config.net.target_price = asset::from_string("1000000.0000 TST"); + config.net.min_price = asset::from_string("0.0000 TST"); + config.net.max_price = asset::from_string("1000000.0000 TST"); config.cpu.current_weight_ratio = rentbw_frac; config.cpu.target_weight_ratio = rentbw_frac / 100; @@ -107,7 +110,8 @@ struct rentbw_tester : eosio_system_tester { config.cpu.target_timestamp = control->head_block_time() + fc::days(100); config.cpu.exponent = 2; config.cpu.decay_secs = fc::days(1).to_seconds(); - config.cpu.target_price = asset::from_string("1000000.0000 TST"); + config.cpu.min_price = asset::from_string("0.0000 TST"); + config.cpu.max_price = asset::from_string("1000000.0000 TST"); config.rent_days = 30; config.min_rent_fee = asset::from_string("1.0000 TST"); @@ -142,7 +146,8 @@ struct rentbw_tester : eosio_system_tester { ("target_timestamp", optional_to_variant(c.target_timestamp)) ("exponent", optional_to_variant(c.exponent)) ("decay_secs", optional_to_variant(c.decay_secs)) - ("target_price", optional_to_variant(c.target_price)) + ("min_price", optional_to_variant(c.min_price)) + ("max_price", optional_to_variant(c.max_price)) ; }; @@ -291,15 +296,25 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { configbw(make_config([&](auto& c) { c.net.exponent = .999; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), configbw(make_config([&](auto& c) { c.net.decay_secs = 0; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price does not have a default value"), - configbw(make_config([&](auto& c) { c.net.target_price = {}; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { - c.net.target_price = asset::from_string("1000000.000 TST"); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price does not have a default value"), + configbw(make_config([&](auto& c) { c.net.max_price = {}; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.net.max_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price must be positive"), + configbw(make_config([&](auto& c) { c.net.max_price = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price must be positive"), + configbw(make_config([&](auto& c) { c.net.max_price = asset::from_string("-1.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.net.min_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price must be non-negative"), + configbw(make_config([&](auto& c) { c.net.min_price = asset::from_string("-1.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price cannot exceed max_price"), + configbw(make_config([&](auto& c) { + c.net.min_price = asset::from_string("3.0000 TST"); + c.net.max_price = asset::from_string("2.0000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("0.0000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.net.target_price = asset::from_string("-1.0000 TST"); }))); // cpu assertions BOOST_REQUIRE_EQUAL(wasm_assert_msg("current_weight_ratio is too large"), @@ -326,15 +341,25 @@ BOOST_FIXTURE_TEST_CASE(config_tests, rentbw_tester) try { configbw(make_config([&](auto& c) { c.cpu.exponent = .999; }))); BOOST_REQUIRE_EQUAL(wasm_assert_msg("decay_secs must be >= 1"), configbw(make_config([&](auto& c) { c.cpu.decay_secs = 0; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price does not have a default value"), - configbw(make_config([&](auto& c) { c.cpu.target_price = {}; }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price doesn't match core symbol"), configbw(make_config([&](auto& c) { - c.cpu.target_price = asset::from_string("1000000.000 TST"); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price does not have a default value"), + configbw(make_config([&](auto& c) { c.cpu.max_price = {}; }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.cpu.max_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price must be positive"), + configbw(make_config([&](auto& c) { c.cpu.max_price = asset::from_string("0.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("max_price must be positive"), + configbw(make_config([&](auto& c) { c.cpu.max_price = asset::from_string("-1.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price doesn't match core symbol"), configbw(make_config([&](auto& c) { + c.cpu.min_price = asset::from_string("1000000.000 TST"); + }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price must be non-negative"), + configbw(make_config([&](auto& c) { c.cpu.min_price = asset::from_string("-1.0000 TST"); }))); + BOOST_REQUIRE_EQUAL(wasm_assert_msg("min_price cannot exceed max_price"), + configbw(make_config([&](auto& c) { + c.cpu.min_price = asset::from_string("3.0000 TST"); + c.cpu.max_price = asset::from_string("2.0000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("0.0000 TST"); }))); - BOOST_REQUIRE_EQUAL(wasm_assert_msg("target_price must be positive"), - configbw(make_config([&](auto& c) { c.cpu.target_price = asset::from_string("-1.0000 TST"); }))); } // config_tests FC_LOG_AND_RETHROW() @@ -479,13 +504,15 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { config.net.target_weight_ratio = rentbw_frac; config.net.assumed_stake_weight = stake_weight; config.net.exponent = 1; - config.net.target_price = asset::from_string("1000000.0000 TST"); + config.net.min_price = asset::from_string("1000000.0000 TST"); + config.net.max_price = asset::from_string("1000000.0000 TST"); config.cpu.current_weight_ratio = rentbw_frac; config.cpu.target_weight_ratio = rentbw_frac; config.cpu.assumed_stake_weight = stake_weight; config.cpu.exponent = 1; - config.cpu.target_price = asset::from_string("1000000.0000 TST"); + config.cpu.min_price = asset::from_string("1000000.0000 TST"); + config.cpu.max_price = asset::from_string("1000000.0000 TST"); config.rent_days = 30; config.min_rent_fee = asset::from_string("1.0000 TST"); @@ -500,6 +527,53 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { BOOST_REQUIRE_EQUAL( t.wasm_assert_msg("market doesn't have resources available"), // t.rentbw(N(bob111111111), N(alice1111111), 30, rentbw_frac, 0, asset::from_string("1.0000 TST"))); + + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_default_config([&](auto& config) { + // weight = stake_weight + config.net.current_weight_ratio = rentbw_frac/2; + config.net.target_weight_ratio = rentbw_frac/2; + + // weight = stake_weight + config.cpu.current_weight_ratio = rentbw_frac/2; + config.cpu.target_weight_ratio = rentbw_frac/2; + }))); + + auto net_weight = stake_weight; + auto cpu_weight = stake_weight; + + t.start_rex(); + t.create_account_with_resources(N(aaaaaaaaaaaa), config::system_account_name, core_sym::from_string("1.0000"), + false, core_sym::from_string("500.0000"), core_sym::from_string("500.0000")); + + // 10%, 20% + // (.1) * 1000000.0000 = 100000.0000 + // (.2) * 1000000.0000 = 200000.0000 + // total = 300000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("300000.0000")); + t.check_rentbw(N(aaaaaaaaaaaa), N(aaaaaaaaaaaa), 30, rentbw_frac * .1, rentbw_frac * .2, + asset::from_string("300000.0000 TST"), net_weight * .1, cpu_weight * .2); + + // Start decay + t.produce_block(fc::days(30) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, .1 * net_weight, 0)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, .2 * cpu_weight, 0)); + + // 2 days of decay from (10%, 20%) to (1.35%, 2.71%) + t.produce_block(fc::days(2) - fc::milliseconds(500)); + BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); + BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, int64_t(.1 * net_weight * exp(-2)), + int64_t(.1 * net_weight * exp(-2)) / 1000)); + BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, int64_t(.2 * cpu_weight * exp(-2)), + int64_t(.2 * cpu_weight * exp(-2)) / 1000)); + + // 2%, 2% + // (0.0135 + 0.02 - 0.0135) * 1000000.0000 = 20000.0000 + // (.02) * 1000000.0000 = 20000.0000 + // total = 40000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("40000.0001")); + t.check_rentbw(N(aaaaaaaaaaaa), N(aaaaaaaaaaaa), 30, rentbw_frac * .02, rentbw_frac * .02, + asset::from_string("40000.0001 TST"), net_weight * .02, cpu_weight * .02); } auto init = [](auto& t, bool rex) { @@ -510,14 +584,14 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { config.net.target_weight_ratio = rentbw_frac / 4; config.net.assumed_stake_weight = stake_weight; config.net.exponent = 2; - config.net.target_price = asset::from_string("1000000.0000 TST"); + config.net.max_price = asset::from_string("2000000.0000 TST"); // weight = stake_weight * 4 / 2 config.cpu.current_weight_ratio = rentbw_frac / 5; config.cpu.target_weight_ratio = rentbw_frac / 5; config.cpu.assumed_stake_weight = stake_weight / 2; config.cpu.exponent = 3; - config.cpu.target_price = asset::from_string("2000000.0000 TST"); + config.cpu.max_price = asset::from_string("6000000.0000 TST"); config.rent_days = 30; config.min_rent_fee = asset::from_string("1.0000 TST"); @@ -605,21 +679,57 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { { rentbw_tester t; init(t, true); - // (.3 ^ 2) * 1000000.0000 = 90000.0000 - // (.4 ^ 3) * 2000000.0000 = 128000.0000 - // total = 218000.0000 + // (.3 ^ 2) * 2000000.0000 / 2 = 90000.0000 + // (.4 ^ 3) * 6000000.0000 / 3 = 128000.0000 + // total = 218000.0000 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("218000.0001")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .3, rentbw_frac * .4, asset::from_string("218000.0001 TST"), net_weight * .3, cpu_weight * .4); - // (.35 ^ 2) * 1000000.0000 - 90000.0000 = 32500.0000 - // (.5 ^ 3) * 2000000.0000 - 128000.0000 = 122000.0000 - // total = 154500.0000 + // (.35 ^ 2) * 2000000.0000 / 2 - 90000.0000 = 32500.0000 + // (.5 ^ 3) * 6000000.0000 / 3 - 128000.0000 = 122000.0000 + // total = 154500.0000 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("154500.0000")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .05, rentbw_frac * .10, asset::from_string("154500.0000 TST"), net_weight * .05, cpu_weight * .10); } + // net:50%, cpu:50% (but with non-zero min_price and also an exponent of 2 to simplify the math) + { + rentbw_tester t; + init(t, true); + BOOST_REQUIRE_EQUAL("", t.configbw(t.make_default_config([&](auto& config) { + config.cpu.exponent = 2; + config.net.min_price = asset::from_string("1200000.0000 TST"); + config.net.max_price = asset::from_string("2000000.0000 TST"); + + config.cpu.exponent = 2; + config.cpu.min_price = asset::from_string("4000000.0000 TST"); + config.cpu.max_price = asset::from_string("6000000.0000 TST"); + }))); + + // At 0% utilization for both NET and CPU, the cost (in TST) for renting an infinitesimal amount of resources (dr) is + // 1200000.0000 * dr for NET and 4000000.0000 * dr for CPU. + // At 50% utilization for both NET and CPU, the cost (in TST for renting an infinitesimal amount of resources (dr) is + // 1600000.0000 * dr for NET and 5000000.0000 * dr for CPU. + + // The fee for renting 50% of NET (starting from 0% utilization) is expected to be somewhere between + // 1200000.0000 * 0.5 (= 600000.0000) and 1600000.0000 * 0.5 (= 800000.0000). + // In fact, the cost ends up being 700000.0000. + + // The fee for renting 50% of CPU (starting from 0% utilization) is expected to be somewhere between + // 4000000.0000 * 0.5 (= 2000000.0000) and 5000000.0000 * 0.5 (= 2500000.0000). + // In fact, the cost ends up being 2250000.0000. + + + // 1200000.0000 * .5 + (800000.0000 / 2) * (.5 ^ 2) = 700000.0000 + // 4000000.0000 * .5 + (2000000.0000 / 2) * (.5 ^ 2) = 2250000.0000 + // total = 2950000.0000 + t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("2950000.0000")); + t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .5, rentbw_frac * .5, + asset::from_string("2950000.0000 TST"), net_weight * .5, cpu_weight * .5); + } + { // net:100%, cpu:100% rentbw_tester t; @@ -644,9 +754,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // immediate renewal: adjusted_utilization doesn't have time to fall // - // 2 * (1.0 ^ 1) * 1000000.0000 = 2000000.0000 - // 3 * (1.0 ^ 2) * 2000000.0000 = 6000000.0000 - // total = 8000000.0000 + // (1.0 ^ 1) * 2000000.0000 = 2000000.0000 + // (1.0 ^ 2) * 6000000.0000 = 6000000.0000 + // total = 8000000.0000 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("8000000.0000")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, asset::from_string("8000000.0000 TST"), 0, 0); @@ -689,9 +799,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // 100% after 2 days of decay // - // [ [2 * ((e^-2) ^ 1)]*(e^-2 - 0.0) + ((1.0) ^ 2) - ((e^-2) ^ 2) ] * 1000000.0000 = 1018315.6389 - // [ [3 * ((e^-2) ^ 2)]*(e^-2 - 0.0) + ((1.0) ^ 3) - ((e^-2) ^ 3) ] * 2000000.0000 = 2009915.0087 - // total = 3028230.6476 + // [ ((e^-2) ^ 1)*(e^-2 - 0.0) + ((1.0) ^ 2)/2 - ((e^-2) ^ 2)/2 ] * 2000000.0000 = 1018315.6389 + // [ ((e^-2) ^ 2)*(e^-2 - 0.0) + ((1.0) ^ 3)/3 - ((e^-2) ^ 3)/3 ] * 6000000.0000 = 2009915.0087 + // total = 3028230.6476 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("3028229.8795")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac, rentbw_frac, asset::from_string("3028229.8795 TST"), net_weight, cpu_weight); @@ -702,9 +812,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { init(t, true); // 10%, 20% - // (.1 ^ 2) * 1000000.0000 = 10000.0000 - // (.2 ^ 3) * 2000000.0000 = 16000.0000 - // total = 26000.0000 + // (.1 ^ 2) * 2000000.0000 / 2 = 10000.0000 + // (.2 ^ 3) * 6000000.0000 / 3 = 16000.0000 + // total = 26000.0000 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("26000.0002")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .1, rentbw_frac * .2, asset::from_string("26000.0002 TST"), net_weight * .1, cpu_weight * .2); @@ -712,9 +822,9 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { t.produce_block(fc::days(15) - fc::milliseconds(500)); // 20%, 20% - // (.3 ^ 2) * 1000000.0000 - 10000.0000 = 80000.0000 - // (.4 ^ 3) * 2000000.0000 - 16000.0000 = 112000.0000 - // total = 192000.0000 + // (.3 ^ 2) * 2000000.0000 / 2 - 10000.0000 = 80000.0000 + // (.4 ^ 3) * 6000000.0000 / 3 - 16000.0000 = 112000.0000 + // total = 192000.0000 t.transfer(config::system_account_name, N(aaaaaaaaaaaa), core_sym::from_string("192000.0001")); t.check_rentbw(N(aaaaaaaaaaaa), N(bbbbbbbbbbbb), 30, rentbw_frac * .2, rentbw_frac * .2, asset::from_string("192000.0001 TST"), net_weight * .2, cpu_weight * .2); @@ -722,7 +832,6 @@ BOOST_AUTO_TEST_CASE(rent_tests) try { // Start decay t.produce_block(fc::days(15) - fc::milliseconds(1000)); BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); - BOOST_REQUIRE_EQUAL("", t.rentbwexec(config::system_account_name, 10)); BOOST_REQUIRE(near(t.get_state().net.adjusted_utilization, .3 * net_weight, 0)); BOOST_REQUIRE(near(t.get_state().cpu.adjusted_utilization, .4 * cpu_weight, 0)); From d09844b94022fcfed9523f71862d49bf776bfee9 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 19 Dec 2019 22:58:18 -0500 Subject: [PATCH 43/45] remove unnecessary precondition comment --- contracts/eosio.system/src/rentbw.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/eosio.system/src/rentbw.cpp b/contracts/eosio.system/src/rentbw.cpp index 6d5d385f3..195f53d37 100644 --- a/contracts/eosio.system/src/rentbw.cpp +++ b/contracts/eosio.system/src/rentbw.cpp @@ -252,7 +252,6 @@ void system_contract::configrentbw(rentbw_config& args) { } // system_contract::configrentbw /** - * @pre 0 == state.min_price.amount (for now) * @pre 0 <= state.min_price.amount <= state.max_price.amount * @pre 0 < state.max_price.amount * @pre 1.0 <= state.exponent From 81bec0ac60abc3dbd9943cb8ce5988c6156a1f4b Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 20 Dec 2019 15:26:55 -0500 Subject: [PATCH 44/45] small tweaks to comments --- .../eosio.system/include/eosio.system/eosio.system.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index f20b34ad6..27f3ef4c8 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -493,10 +493,12 @@ namespace eosiosystem { // by 63%. Do not specify to preserve the existing setting or // use the default. std::optional min_price; // Fee needed to rent the entire resource market weight at the - // minimum price. Do not specify to preserve the existing + // minimum price. For example, this could be set to 0.005% of + // total token supply. Do not specify to preserve the existing // setting or use the default. std::optional max_price; // Fee needed to rent the entire resource market weight at the - // maximum price. Do not specify to preserve the existing + // maximum price. For example, this could be set to 10% of total + // total token supply. Do not specify to preserve the existing // setting (no default exists). EOSLIB_SERIALIZE( rentbw_config_resource, (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight) @@ -507,9 +509,9 @@ namespace eosiosystem { rentbw_config_resource net; // NET market configuration rentbw_config_resource cpu; // CPU market configuration std::optional rent_days; // `rentbw` `days` argument must match this. Do not specify to preserve the - // existing setting or use the default. + // existing setting or use the default. std::optional min_rent_fee; // Rental fees below this amount are rejected. Do not specify to preserve the - // existing setting (no default exists). + // existing setting (no default exists). EOSLIB_SERIALIZE( rentbw_config, (net)(cpu)(rent_days)(min_rent_fee) ) }; From 6c8451b6c135a25fd20273df358742b9923b8b40 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 20 Dec 2019 15:27:59 -0500 Subject: [PATCH 45/45] quick fix to comment --- contracts/eosio.system/include/eosio.system/eosio.system.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 27f3ef4c8..0b5d6c21c 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -498,7 +498,7 @@ namespace eosiosystem { // setting or use the default. std::optional max_price; // Fee needed to rent the entire resource market weight at the // maximum price. For example, this could be set to 10% of total - // total token supply. Do not specify to preserve the existing + // token supply. Do not specify to preserve the existing // setting (no default exists). EOSLIB_SERIALIZE( rentbw_config_resource, (current_weight_ratio)(target_weight_ratio)(assumed_stake_weight)