From 80ae56598692f42bef74b92764ecf8d194041290 Mon Sep 17 00:00:00 2001 From: Anton Perkov Date: Wed, 28 Feb 2018 19:27:55 -0500 Subject: [PATCH] stake/unstake storage, producers vote for storage_reserve_ratio --- contracts/eosio.system/common.hpp | 31 ++++++ contracts/eosio.system/delegate_bandwith.hpp | 101 ++++++++++++++----- contracts/eosio.system/voting.hpp | 76 +++++++------- contracts/eosiolib/generic_currency.hpp | 5 + contracts/eosiolib/privileged.hpp | 1 + contracts/eosiolib/singleton.hpp | 2 +- contracts/eosiolib/token.hpp | 42 +++++++- 7 files changed, 191 insertions(+), 67 deletions(-) create mode 100644 contracts/eosio.system/common.hpp diff --git a/contracts/eosio.system/common.hpp b/contracts/eosio.system/common.hpp new file mode 100644 index 000000000..a0d25cdb8 --- /dev/null +++ b/contracts/eosio.system/common.hpp @@ -0,0 +1,31 @@ +#pragma once +#include + +#include +#include +#include +#include + +namespace eosiosystem { + + template + class common { + public: + static constexpr account_name system_account = SystemAccount; + typedef eosio::generic_currency< eosio::token > currency; + typedef typename currency::token_type system_token_type; + + struct eosio_parameters : eosio::blockchain_parameters { + uint32_t inflation_rate = 0; // inflation coefficient * 10000 (i.e. inflation in percent * 100) + uint32_t storage_reserve_ratio = 1; // ratio * 1000 + uint64_t total_storage_bytes_reserved = 0; + system_token_type total_storage_stake; + eosio_parameters() { bzero(this, sizeof(*this)); } + + EOSLIB_SERIALIZE_DERIVED( eosio_parameters, eosio::blockchain_parameters, (inflation_rate)(storage_reserve_ratio) + (storage_reserve_ratio)(total_storage_bytes_reserved)(total_storage_stake) ) + }; + + typedef eosio::singleton eosio_parameters_singleton; + }; +} diff --git a/contracts/eosio.system/delegate_bandwith.hpp b/contracts/eosio.system/delegate_bandwith.hpp index 29922f960..3c585005b 100644 --- a/contracts/eosio.system/delegate_bandwith.hpp +++ b/contracts/eosio.system/delegate_bandwith.hpp @@ -3,6 +3,7 @@ * @copyright defined in eos/LICENSE.txt */ #pragma once +#include "common.hpp" #include #include @@ -27,19 +28,22 @@ namespace eosiosystem { template class delegate_bandwith { public: - static const account_name system_account = SystemAccount; - typedef eosio::generic_currency< eosio::token > currency; - typedef typename currency::token_type system_token_type; + static constexpr account_name system_account = SystemAccount; + using currency = typename common::currency; + using system_token_type = typename common::system_token_type; + using eosio_parameters = typename common::eosio_parameters; + using eosio_parameters_singleton = typename common::eosio_parameters_singleton; struct total_resources { account_name owner; typename currency::token_type total_net_weight; - typename currency::token_type total_cpu_weight; - uint32_t total_ram = 0; + typename currency::token_type total_cpu_weight; + typename currency::token_type total_storage_stake; + uint64_t total_storage_bytes = 0; uint64_t primary_key()const { return owner; } - EOSLIB_SERIALIZE( total_resources, (owner)(total_net_weight)(total_cpu_weight)(total_ram) ) + EOSLIB_SERIALIZE( total_resources, (owner)(total_net_weight)(total_cpu_weight)(total_storage_bytes) ) }; @@ -50,7 +54,9 @@ namespace eosiosystem { account_name from; account_name to; typename currency::token_type net_weight; - typename currency::token_type cpu_weight; + typename currency::token_type cpu_weight; + typename currency::token_type storage_stake; + uint64_t storage_bytes = 0; uint32_t start_pending_net_withdraw = 0; typename currency::token_type pending_net_withdraw; @@ -63,7 +69,7 @@ namespace eosiosystem { uint64_t primary_key()const { return to; } - EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight) + EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight)(storage_stake)(storage_bytes) (start_pending_net_withdraw)(pending_net_withdraw)(deferred_net_withdraw_handler) (start_pending_cpu_withdraw)(pending_cpu_withdraw)(deferred_cpu_withdraw_handler) ) @@ -82,6 +88,7 @@ namespace eosiosystem { account_name receiver; typename currency::token_type stake_net_quantity; typename currency::token_type stake_cpu_quantity; + typename currency::token_type stake_storage_quantity; EOSLIB_SERIALIZE( delegatebw, (from)(receiver)(stake_net_quantity)(stake_cpu_quantity) ) @@ -92,8 +99,9 @@ namespace eosiosystem { account_name receiver; typename currency::token_type unstake_net_quantity; typename currency::token_type unstake_cpu_quantity; + uint64_t unstake_storage_bytes; - EOSLIB_SERIALIZE( undelegatebw, (from)(receiver)(unstake_net_quantity)(unstake_cpu_quantity) ) + EOSLIB_SERIALIZE( undelegatebw, (from)(receiver)(unstake_net_quantity)(unstake_cpu_quantity)(unstake_storage_bytes) ) }; /// new id options: @@ -104,7 +112,7 @@ namespace eosiosystem { eosio_assert( del.stake_cpu_quantity.quantity >= 0, "must stake a positive amount" ); eosio_assert( del.stake_net_quantity.quantity >= 0, "must stake a positive amount" ); - auto total_stake = del.stake_cpu_quantity + del.stake_net_quantity; + auto total_stake = del.stake_cpu_quantity + del.stake_net_quantity + del.stake_storage_quantity; eosio_assert( total_stake.quantity >= 0, "must stake a positive amount" ); @@ -115,19 +123,38 @@ namespace eosiosystem { //eosio_assert( is_account( del.receiver ), "can only delegate resources to an existing account" ); + auto parameters = eosio_parameters_singleton::get_or_default(); + auto token_supply = currency::get_total_supply();//.quantity; + //if ( token_supply == 0 || parameters not set) { what to do? } + + //make sure that there is no posibility of overflow here + uint64_t storage_bytes_estimated = ( parameters.max_storage_size - parameters.total_storage_bytes_reserved ) + * parameters.storage_reserve_ratio * del.stake_storage_quantity + / ( token_supply - parameters.total_storage_stake ) / 1000 /* reserve ratio coefficient */; + + uint64_t storage_bytes = ( parameters.max_storage_size - parameters.total_storage_bytes_reserved - storage_bytes_estimated ) + * parameters.storage_reserve_ratio * del.stake_storage_quantity + / ( token_supply - del.stake_storage_quantity - parameters.total_storage_stake ) / 1000 /* reserve ratio coefficient */; + + eosio_assert( 0 < storage_bytes, "stake is too small to increase memory even for 1 byte" ); + auto itr = del_index.find( del.receiver); if( itr != nullptr ) { del_index.emplace( del.from, [&]( auto& dbo ){ - dbo.from = del.from; - dbo.to = del.receiver; - dbo.net_weight = del.stake_net_quantity; - dbo.cpu_weight = del.stake_cpu_quantity; + dbo.from = del.from; + dbo.to = del.receiver; + dbo.net_weight = del.stake_net_quantity; + dbo.cpu_weight = del.stake_cpu_quantity; + dbo.storage_stake = del.stake_storage_quantity; + dbo.storage_bytes = storage_bytes; }); } else { del_index.update( *itr, del.from, [&]( auto& dbo ){ - dbo.net_weight = del.stake_net_quantity; - dbo.cpu_weight = del.stake_cpu_quantity; + dbo.net_weight += del.stake_net_quantity; + dbo.cpu_weight += del.stake_cpu_quantity; + dbo.storage_stake += del.stake_storage_quantity; + dbo.storage_bytes += storage_bytes; }); } @@ -135,28 +162,33 @@ namespace eosiosystem { if( tot_itr == nullptr ) { tot_itr = &total_index.emplace( del.from, [&]( auto& tot ) { tot.owner = del.receiver; - tot.total_net_weight += del.stake_net_quantity; - tot.total_cpu_weight += del.stake_cpu_quantity; + tot.total_net_weight = del.stake_net_quantity; + tot.total_cpu_weight = del.stake_cpu_quantity; + tot.total_storage_stake = del.stake_storage_quantity; + tot.total_storage_bytes = storage_bytes; }); } else { total_index.update( *tot_itr, 0, [&]( auto& tot ) { - tot.total_net_weight += del.stake_net_quantity; - tot.total_cpu_weight += del.stake_cpu_quantity; + tot.total_net_weight += del.stake_net_quantity; + tot.total_cpu_weight += del.stake_cpu_quantity; + tot.total_storage_stake += del.stake_storage_quantity; + tot.total_storage_bytes += storage_bytes; }); } - set_resource_limits( tot_itr->owner, tot_itr->total_ram, tot_itr->total_net_weight.quantity, tot_itr->total_cpu_weight.quantity, 0 ); + set_resource_limits( tot_itr->owner, tot_itr->total_storage_bytes, tot_itr->total_net_weight.quantity, tot_itr->total_cpu_weight.quantity, 0 ); currency::inline_transfer( del.from, SystemAccount, total_stake, "stake bandwidth" ); + + parameters.total_storage_bytes_reserved += storage_bytes; + parameters.total_storage_stake += del.stake_storage_quantity; + eosio_parameters_singleton::set(parameters); } // delegatebw static void on( const undelegatebw& del ) { eosio_assert( del.unstake_cpu_quantity.quantity >= 0, "must stake a positive amount" ); eosio_assert( del.unstake_net_quantity.quantity >= 0, "must stake a positive amount" ); - auto total_stake = del.unstake_cpu_quantity + del.unstake_net_quantity; - eosio_assert( total_stake.quantity >= 0, "must stake a positive amount" ); - require_auth( del.from ); del_bandwidth_index_type del_index( SystemAccount, del.from ); @@ -168,22 +200,35 @@ namespace eosiosystem { eosio_assert( dbw.net_weight >= del.unstake_net_quantity, "insufficient staked net bandwidth" ); eosio_assert( dbw.cpu_weight >= del.unstake_cpu_quantity, "insufficient staked cpu bandwidth" ); + const auto& totals = total_index.get( del.receiver ); + system_token_type storage_stake_decrease = totals.total_storage_stake * del.unstake_storage_bytes / totals.total_storage_bytes; + + auto total_refund = del.unstake_cpu_quantity + del.unstake_net_quantity + storage_stake_decrease; + eosio_assert( total_refund.quantity >= 0, "must stake a positive amount" ); + del_index.update( dbw, del.from, [&]( auto& dbo ){ dbo.net_weight -= del.unstake_net_quantity; dbo.cpu_weight -= del.unstake_cpu_quantity; - + dbo.storage_stake -= storage_stake_decrease; + dbo.storage_bytes -= del.unstake_storage_bytes; }); - const auto& totals = total_index.get( del.receiver ); total_index.update( totals, 0, [&]( auto& tot ) { tot.total_net_weight -= del.unstake_net_quantity; tot.total_cpu_weight -= del.unstake_cpu_quantity; + tot.total_storage_stake -= storage_stake_decrease; + tot.total_storage_bytes -= del.unstake_storage_bytes; }); - set_resource_limits( totals.owner, totals.total_ram, totals.total_net_weight.quantity, totals.total_cpu_weight.quantity, 0 ); + set_resource_limits( totals.owner, totals.total_storage_bytes, totals.total_net_weight.quantity, totals.total_cpu_weight.quantity, 0 ); /// TODO: implement / enforce time delays on withdrawing - currency::inline_transfer( SystemAccount, del.from, total_stake, "unstake bandwidth" ); + currency::inline_transfer( SystemAccount, del.from, total_refund, "unstake bandwidth" ); + + auto parameters = eosio_parameters_singleton::get(); + parameters.total_storage_bytes_reserved -= del.unstake_storage_bytes; + parameters.total_storage_stake -= storage_stake_decrease; + eosio_parameters_singleton::set( parameters ); } // undelegatebw }; } diff --git a/contracts/eosio.system/voting.hpp b/contracts/eosio.system/voting.hpp index 74d0e8521..58d67ab7c 100644 --- a/contracts/eosio.system/voting.hpp +++ b/contracts/eosio.system/voting.hpp @@ -3,6 +3,8 @@ * @copyright defined in eos/LICENSE.txt */ #pragma once +#include "common.hpp" + #include #include #include @@ -26,30 +28,25 @@ namespace eosiosystem { using eosio::singleton; using eosio::transaction; + template class voting { public: - static const account_name system_account = SystemAccount; - typedef eosio::generic_currency< eosio::token > currency; - typedef typename currency::token_type system_token_type; + static constexpr account_name system_account = SystemAccount; + using currency = typename common::currency; + using system_token_type = typename common::system_token_type; + using eosio_parameters = typename common::eosio_parameters; + using eosio_parameters_singleton = typename common::eosio_parameters_singleton; static constexpr uint32_t max_unstake_requests = 10; static constexpr uint32_t unstake_pay_period = 7*24*3600; // one per week static constexpr uint32_t unstake_payments = 26; // during 26 weeks - struct producer_preferences : eosio::blockchain_parameters { - uint32_t inflation_rate; // inflation coefficient * 10000 (i.e. inflation in percent * 1000) - - producer_preferences() { bzero(this, sizeof(*this)); } - - EOSLIB_SERIALIZE_DERIVED( producer_preferences, eosio::blockchain_parameters, (inflation_rate) ) - }; - struct producer_info { account_name owner; uint64_t padding = 0; uint128_t total_votes = 0; - producer_preferences prefs; + eosio_parameters prefs; eosio::bytes packed_key; /// a packed public key object uint64_t primary_key()const { return owner; } @@ -63,7 +60,6 @@ namespace eosiosystem { indexed_by > > producers_table; - typedef singleton inflation_singleton; struct account_votes { account_name owner = 0; @@ -99,7 +95,7 @@ namespace eosiosystem { ACTION( SystemAccount, register_producer ) { account_name producer; bytes producer_key; - producer_preferences prefs; + eosio_parameters prefs; EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) ) }; @@ -132,15 +128,15 @@ namespace eosiosystem { }); } - ACTION( SystemAccount, change_producer_preferences ) { + ACTION( SystemAccount, change_eosio_parameters ) { account_name producer; bytes producer_key; - producer_preferences prefs; + eosio_parameters prefs; EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) ) }; - static void on( const change_producer_preferences& change) { + static void on( const change_eosio_parameters& change) { require_auth( change.producer ); producers_table producers_tbl( SystemAccount, SystemAccount ); @@ -218,6 +214,7 @@ namespace eosiosystem { std::array max_inline_action_size; std::array max_generated_transaction_size; std::array inflation_rate; + std::array storage_reserve_ratio; std::array elected; @@ -244,6 +241,7 @@ namespace eosiosystem { max_inline_action_size[n] = it->prefs.max_inline_action_size; max_generated_transaction_size[n] = it->prefs.max_generated_transaction_size; + storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio; inflation_rate[n] = it->prefs.inflation_rate; ++n; } @@ -256,25 +254,31 @@ namespace eosiosystem { set_active_producers( elected.data(), n ); size_t median = n/2; - ::blockchain_parameters concensus = { - target_block_size[median], - max_block_size[median], - target_block_acts_per_scope[median], - max_block_acts_per_scope[median], - target_block_acts[median], - max_block_acts[median], - max_storage_size[median], - max_transaction_lifetime[median], - max_transaction_exec_time[median], - max_authority_depth[median], - max_inline_depth[median], - max_inline_action_size[median], - max_generated_transaction_size[median] - }; - - set_blockchain_parameters(&concensus); - - inflation_singleton::set( inflation_rate[median] ); + auto parameters = eosio_parameters_singleton::get(); + + parameters.target_block_size = target_block_size[median]; + parameters.max_block_size = max_block_size[median]; + parameters.target_block_acts_per_scope = target_block_acts_per_scope[median]; + parameters.max_block_acts_per_scope = max_block_acts_per_scope[median]; + parameters.target_block_acts = target_block_acts[median]; + parameters.max_block_acts = max_block_acts[median]; + parameters.max_storage_size = max_storage_size[median]; + parameters.max_transaction_lifetime = max_transaction_lifetime[median]; + parameters.max_transaction_exec_time = max_transaction_exec_time[median]; + parameters.max_authority_depth = max_authority_depth[median]; + parameters.max_inline_depth = max_inline_depth[median]; + parameters.max_inline_action_size = max_inline_action_size[median]; + parameters.max_generated_transaction_size = max_generated_transaction_size[median]; + parameters.storage_reserve_ratio = storage_reserve_ratio[median]; + parameters.inflation_rate = inflation_rate[median]; + + if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) { + parameters.max_storage_size = parameters.total_storage_bytes_reserved; + } + + set_blockchain_parameters(¶meters); + + eosio_parameters_singleton::set( parameters ); } static void on( const stake_vote& sv ) { diff --git a/contracts/eosiolib/generic_currency.hpp b/contracts/eosiolib/generic_currency.hpp index 460d4695d..e3db83bbb 100644 --- a/contracts/eosiolib/generic_currency.hpp +++ b/contracts/eosiolib/generic_currency.hpp @@ -134,6 +134,11 @@ namespace eosio { act.send(); } + static token_type get_total_supply() { + stats t( code, code ); + auto ptr = t.find( symbol ); + return ptr ? ptr->supply : token_type(0); + } static void apply( account_name c, action_name act) { eosio::dispatch(c,act); diff --git a/contracts/eosiolib/privileged.hpp b/contracts/eosiolib/privileged.hpp index 22d1242f5..e91c3a6e0 100644 --- a/contracts/eosiolib/privileged.hpp +++ b/contracts/eosiolib/privileged.hpp @@ -1,3 +1,4 @@ +#pragma once #include "privileged.h" #include "serialize.hpp" diff --git a/contracts/eosiolib/singleton.hpp b/contracts/eosiolib/singleton.hpp index 962b4a44f..51e332457 100644 --- a/contracts/eosiolib/singleton.hpp +++ b/contracts/eosiolib/singleton.hpp @@ -28,7 +28,7 @@ namespace eosio { *reinterpret_cast(temp) = SingletonName; auto read = load_i64( Code, scope, SingletonName, temp, sizeof(temp) ); eosio_assert( read > 0, "singleton does not exist" ); - return unpack( temp + sizeof(SingletonName), read ); + return unpack( temp + sizeof(SingletonName), size_t(read) ); } static T get_or_default( scope_name scope = Code, const T& def = T() ) { diff --git a/contracts/eosiolib/token.hpp b/contracts/eosiolib/token.hpp index e319d8f76..da737c05a 100644 --- a/contracts/eosiolib/token.hpp +++ b/contracts/eosiolib/token.hpp @@ -127,7 +127,7 @@ namespace eosio { /** * Multiplies token and integer * Throws an exception if overflow - * @brief Adds quantity of two tokens and return a new token + * @brief Multiplies quantity of two tokens and return a new token * @param a token to be multiplied * @param b multipier * @return result of addition as a new token @@ -141,7 +141,7 @@ namespace eosio { /** * Multiplies token and integer * Throws an exception if overflow - * @brief Adds quantity of two tokens and return a new token + * @brief Multiplies quantity of two tokens and return a new token * @param a token to be multiplied * @param b multipier * @return result of addition as a new token @@ -152,6 +152,44 @@ namespace eosio { return result; } + /** + * Divides quantity of token by an integer + * Throws an exception if overflow + * @brief Divides quantity of token by an integer + * @param a multipier + * @return this token after addition + */ + token& operator/=( uint64_t a ) { + quantity /= a; + return *this; + } + + /** + * Divides token and integer + * Throws an exception if overflow + * @brief Divides quantity of two tokens and return a new token + * @param a token to be multiplied + * @param b multipier + * @return result of addition as a new token + */ + inline friend token operator/( const token& a, uint64_t b ) { + token result = a; + result /= b; + return result; + } + + /** + * Divides two tokens + * Throws an exception if overflow + * @brief Divides quantity of two tokens and return a new token + * @param a token + * @param b token + * @return result of addition as a new token + */ + inline friend NumberType operator/( const token& a, const token& b ) { + return a.quantity / b.quantity; + } + /** * Less than or equal to comparison operator * @brief Less than or equal to comparison operator -- GitLab