From e649b1d338d130441700b4a2cb0ff163cf73210b Mon Sep 17 00:00:00 2001 From: maslenitsa93 Date: Tue, 11 Sep 2018 01:03:39 +0300 Subject: [PATCH] New limits algorithm for comments and votes #533 #825 --- libraries/api/chain_api_properties.cpp | 4 ++ .../golos/api/chain_api_properties.hpp | 9 ++- libraries/chain/database.cpp | 21 ++++++- libraries/chain/hardfork.d/0_19.hf | 1 + .../include/golos/chain/account_object.hpp | 2 + libraries/chain/steem_evaluator.cpp | 55 +++++++++++++++++-- .../include/golos/protocol/config.hpp | 10 ++++ .../golos/protocol/steem_operations.hpp | 33 +++++++++-- libraries/protocol/steem_operations.cpp | 4 ++ .../wallet/include/golos/wallet/wallet.hpp | 10 +++- libraries/wallet/wallet.cpp | 14 ++++- 11 files changed, 145 insertions(+), 18 deletions(-) diff --git a/libraries/api/chain_api_properties.cpp b/libraries/api/chain_api_properties.cpp index f658efcf4c..dc40f47fbf 100644 --- a/libraries/api/chain_api_properties.cpp +++ b/libraries/api/chain_api_properties.cpp @@ -19,6 +19,10 @@ namespace golos { namespace api { max_referral_interest_rate = src.max_referral_interest_rate; max_referral_term_sec = src.max_referral_term_sec; max_referral_break_fee = src.max_referral_break_fee; + comments_window = src.comments_window; + comments_per_window = src.comments_per_window; + votes_window = src.votes_window; + votes_per_window = src.votes_per_window; auction_window_size = src.auction_window_size; } } diff --git a/libraries/api/include/golos/api/chain_api_properties.hpp b/libraries/api/include/golos/api/chain_api_properties.hpp index a6c73c6d5e..98c5301666 100644 --- a/libraries/api/include/golos/api/chain_api_properties.hpp +++ b/libraries/api/include/golos/api/chain_api_properties.hpp @@ -24,6 +24,12 @@ namespace golos { namespace api { fc::optional max_referral_interest_rate; fc::optional max_referral_term_sec; fc::optional max_referral_break_fee; + + fc::optional comments_window; + fc::optional comments_per_window; + fc::optional votes_window; + fc::optional votes_per_window; + fc::optional auction_window_size; }; @@ -34,4 +40,5 @@ FC_REFLECT( (account_creation_fee)(maximum_block_size)(sbd_interest_rate) (create_account_min_golos_fee)(create_account_min_delegation) (create_account_delegation_time)(min_delegation) - (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee)(auction_window_size)) + (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee) + (comments_window)(comments_per_window)(votes_window)(votes_per_window)(auction_window_size)) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index e08d21fd96..e23fb8f548 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1878,14 +1878,29 @@ namespace golos { namespace chain { chain_properties_19 median_props; + auto median = active.size() / 2; + auto calc_median = [&](auto&& param) { std::nth_element( - active.begin(), active.begin() + active.size() / 2, active.end(), + active.begin(), active.begin() + median, active.end(), [&](const auto* a, const auto* b) { return a->props.*param < b->props.*param; } ); - median_props.*param = active[active.size() / 2]->props.*param; + median_props.*param = active[median]->props.*param; + }; + + auto calc_median_battery = [&](auto&& window, auto&& items) { + std::nth_element( + active.begin(), active.begin() + median, active.end(), + [&](const auto* a, const auto* b) { + auto a_consumption = a->props.*window / a->props.*items; + auto b_consumption = b->props.*window / b->props.*items; + return std::tie(a_consumption, a->props.*items) < std::tie(b_consumption, b->props.*items); + } + ); + median_props.*window = active[median]->props.*window; + median_props.*items = active[median]->props.*items; }; calc_median(&chain_properties_17::account_creation_fee); @@ -1899,6 +1914,8 @@ namespace golos { namespace chain { calc_median(&chain_properties_19::max_referral_interest_rate); calc_median(&chain_properties_19::max_referral_term_sec); calc_median(&chain_properties_19::max_referral_break_fee); + calc_median_battery(&chain_properties_19::comments_window, &chain_properties_19::comments_per_window); + calc_median_battery(&chain_properties_19::votes_window, &chain_properties_19::votes_per_window); modify(wso, [&](witness_schedule_object &_wso) { _wso.median_props = median_props; diff --git a/libraries/chain/hardfork.d/0_19.hf b/libraries/chain/hardfork.d/0_19.hf index ff6aae249f..25ddf693e8 100644 --- a/libraries/chain/hardfork.d/0_19.hf +++ b/libraries/chain/hardfork.d/0_19.hf @@ -2,6 +2,7 @@ #define STEEMIT_HARDFORK_0_19 19 #define STEEMIT_HARDFORK_0_19__898 (STEEMIT_HARDFORK_0_19) // Add votable auction window size #define STEEMIT_HARDFORK_0_19__295 (STEEMIT_HARDFORK_0_19) // Referral program implemented +#define STEEMIT_HARDFORK_0_19__533 (STEEMIT_HARDFORK_0_19) // Leaky algorithm for comment and vote bandwidth #ifdef STEEMIT_BUILD_TESTNET #define STEEMIT_HARDFORK_0_19_TIME 1534755600 // 20 aug 2018 12:00:00 MSK diff --git a/libraries/chain/include/golos/chain/account_object.hpp b/libraries/chain/include/golos/chain/account_object.hpp index 632aa04214..9e8c5db171 100644 --- a/libraries/chain/include/golos/chain/account_object.hpp +++ b/libraries/chain/include/golos/chain/account_object.hpp @@ -50,6 +50,8 @@ class account_object bool can_vote = true; uint16_t voting_power = STEEMIT_100_PERCENT; ///< current voting power of this account, it falls after every vote + uint16_t comments_capacity = STEEMIT_COMMENTS_WINDOW; + uint16_t voting_capacity = STEEMIT_VOTES_WINDOW; time_point_sec last_vote_time; ///< used to increase the voting power of this account the longer it goes without voting. asset balance = asset(0, STEEM_SYMBOL); ///< total liquid shares held by this account diff --git a/libraries/chain/steem_evaluator.cpp b/libraries/chain/steem_evaluator.cpp index 7b103dbdcf..58034626c9 100644 --- a/libraries/chain/steem_evaluator.cpp +++ b/libraries/chain/steem_evaluator.cpp @@ -587,6 +587,8 @@ namespace golos { namespace chain { logic_exception::cannot_update_comment_because_nothing_changed, "Cannot update comment because nothing appears to be changing."); + const auto& mprops = _db.get_witness_schedule_object().median_props; + const auto &by_permlink_idx = _db.get_index().indices().get(); auto itr = by_permlink_idx.find(boost::make_tuple(o.author, o.permlink)); @@ -631,7 +633,29 @@ namespace golos { namespace chain { }); } - if (_db.has_hardfork(STEEMIT_HARDFORK_0_12__176)) { + auto elapsed_seconds = (now - auth.last_post).to_seconds(); + + if (_db.has_hardfork(STEEMIT_HARDFORK_0_19__533)) { + auto consumption = mprops.comments_window / mprops.comments_per_window; + + auto regenerated_capacity = std::min(uint32_t(mprops.comments_window), uint32_t(elapsed_seconds)); + auto current_capacity = std::min(uint16_t(auth.comments_capacity + regenerated_capacity), mprops.comments_window); + + if (o.parent_author == STEEMIT_ROOT_POST_PARENT) { + GOLOS_CHECK_BANDWIDTH(now, band->last_bandwidth_update + STEEMIT_MIN_ROOT_COMMENT_INTERVAL, + bandwidth_exception::post_bandwidth, + "You may only post once every 5 minutes."); + } else { + GOLOS_CHECK_BANDWIDTH(current_capacity, consumption, + bandwidth_exception::comment_bandwidth, + "You may only comment ${comments_per_window} times in ${comments_window} seconds.", + ("comments_per_window", mprops.comments_per_window)("comments_window", mprops.comments_window)); + } + + db().modify(auth, [&](account_object &a) { + a.comments_capacity = current_capacity - consumption; + }); + } else if (_db.has_hardfork(STEEMIT_HARDFORK_0_12__176)) { if (o.parent_author == STEEMIT_ROOT_POST_PARENT) GOLOS_CHECK_BANDWIDTH(now, band->last_bandwidth_update + STEEMIT_MIN_ROOT_COMMENT_INTERVAL, bandwidth_exception::post_bandwidth, @@ -684,7 +708,11 @@ namespace golos { namespace chain { db().modify(auth, [&](account_object &a) { a.last_post = now; - a.post_count++; + if (o.parent_author != STEEMIT_ROOT_POST_PARENT) { + a.comment_count++; + } else { + a.post_count++; + } }); bool referrer_to_delete = false; @@ -1249,6 +1277,8 @@ namespace golos { namespace chain { const auto& comment = _db.get_comment(o.author, o.permlink); const auto& voter = _db.get_account(o.voter); + const auto& mprops = _db.get_witness_schedule_object().median_props; + GOLOS_CHECK_LOGIC(!(voter.owner_challenged || voter.active_challenged), logic_exception::account_is_currently_challenged, "Account \"${account}\" is currently challenged", ("account", voter.name)); @@ -1285,10 +1315,26 @@ namespace golos { namespace chain { const auto& comment_vote_idx = _db.get_index().indices().get(); auto itr = comment_vote_idx.find(std::make_tuple(comment.id, voter.id)); - int64_t elapsed_seconds = (_db.head_block_time() - voter.last_vote_time).to_seconds(); + auto elapsed_seconds = (_db.head_block_time() - voter.last_vote_time).to_seconds(); + + if (_db.has_hardfork(STEEMIT_HARDFORK_0_19__533)) { + auto consumption = mprops.votes_window / mprops.votes_per_window; + + auto regenerated_capacity = std::min(uint32_t(mprops.votes_window), uint32_t(elapsed_seconds)); + auto current_capacity = std::min(uint16_t(voter.voting_capacity + regenerated_capacity), mprops.votes_window); + + GOLOS_CHECK_BANDWIDTH(current_capacity, consumption, + bandwidth_exception::vote_bandwidth, + "Can only vote ${votes_per_window} times in ${votes_window} seconds.", + ("votes_per_window", mprops.votes_per_window)("votes_window", mprops.votes_window)); - GOLOS_CHECK_BANDWIDTH(_db.head_block_time(), voter.last_vote_time + STEEMIT_MIN_VOTE_INTERVAL_SEC-1, + _db.modify(voter, [&](account_object &a) { + a.voting_capacity = current_capacity - consumption; + }); + } else { + GOLOS_CHECK_BANDWIDTH(_db.head_block_time(), voter.last_vote_time + STEEMIT_MIN_VOTE_INTERVAL_SEC-1, bandwidth_exception::vote_bandwidth, "Can only vote once every 3 seconds."); + } int64_t regenerated_power = (STEEMIT_100_PERCENT * elapsed_seconds) / @@ -1655,7 +1701,6 @@ namespace golos { namespace chain { _db.adjust_rshares2(comment, old_rshares, new_rshares); } - } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/protocol/include/golos/protocol/config.hpp b/libraries/protocol/include/golos/protocol/config.hpp index 76023e092c..3e85862d04 100644 --- a/libraries/protocol/include/golos/protocol/config.hpp +++ b/libraries/protocol/include/golos/protocol/config.hpp @@ -81,6 +81,11 @@ #define STEEMIT_POST_MAX_BANDWIDTH (4*STEEMIT_100_PERCENT) // 2 posts per 1 days, average 1 every 12 hours #define STEEMIT_POST_WEIGHT_CONSTANT (uint64_t(STEEMIT_POST_MAX_BANDWIDTH) * STEEMIT_POST_MAX_BANDWIDTH) +#define STEEMIT_COMMENTS_WINDOW 200 +#define STEEMIT_COMMENTS_PER_WINDOW 10 +#define STEEMIT_VOTES_WINDOW 200 // For testnet +#define STEEMIT_VOTES_PER_WINDOW 10 + #define STEEMIT_MAX_ACCOUNT_WITNESS_VOTES 30 #define STEEMIT_100_PERCENT 10000 @@ -297,6 +302,11 @@ #define STEEMIT_POST_MAX_BANDWIDTH (4*STEEMIT_100_PERCENT) // 2 posts per 1 days, average 1 every 12 hours #define STEEMIT_POST_WEIGHT_CONSTANT (uint64_t(STEEMIT_POST_MAX_BANDWIDTH) * STEEMIT_POST_MAX_BANDWIDTH) +#define STEEMIT_COMMENTS_WINDOW 200 +#define STEEMIT_COMMENTS_PER_WINDOW 10 +#define STEEMIT_VOTES_WINDOW 15 +#define STEEMIT_VOTES_PER_WINDOW 5 + #define STEEMIT_MAX_ACCOUNT_WITNESS_VOTES 30 #define STEEMIT_100_PERCENT 10000 diff --git a/libraries/protocol/include/golos/protocol/steem_operations.hpp b/libraries/protocol/include/golos/protocol/steem_operations.hpp index afea4831f4..471e7dda6d 100644 --- a/libraries/protocol/include/golos/protocol/steem_operations.hpp +++ b/libraries/protocol/include/golos/protocol/steem_operations.hpp @@ -518,11 +518,6 @@ namespace golos { namespace protocol { */ struct chain_properties_19: public chain_properties_18 { - /** - * Auction window size - */ - uint32_t auction_window_size = STEEMIT_REVERSE_AUCTION_WINDOW_SECONDS; - /** * Maximum percent of referral deductions */ @@ -538,6 +533,31 @@ namespace golos { namespace protocol { */ asset max_referral_break_fee = GOLOS_DEFAULT_REFERRAL_BREAK_FEE; + /** + * Time window for commenting by account + */ + uint16_t comments_window = STEEMIT_COMMENTS_WINDOW; + + /** + * Maximum count of comments per one window by account + */ + uint16_t comments_per_window = STEEMIT_COMMENTS_PER_WINDOW; + + /** + * Time window for voting by account + */ + uint16_t votes_window = STEEMIT_VOTES_WINDOW; + + /** + * Maximum count of votes per one window by account + */ + uint16_t votes_per_window = STEEMIT_VOTES_PER_WINDOW; + + /** + * Auction window size + */ + uint32_t auction_window_size = STEEMIT_REVERSE_AUCTION_WINDOW_SECONDS; + void validate() const; chain_properties_19& operator=(const chain_properties_17& src) { @@ -1216,7 +1236,8 @@ FC_REFLECT_DERIVED( (create_account_delegation_time)(min_delegation)) FC_REFLECT_DERIVED( (golos::protocol::chain_properties_19), ((golos::protocol::chain_properties_18)), - (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee)(auction_window_size)) + (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee) + (comments_window)(comments_per_window)(votes_window)(votes_per_window)(auction_window_size)) FC_REFLECT_TYPENAME((golos::protocol::versioned_chain_properties)) diff --git a/libraries/protocol/steem_operations.cpp b/libraries/protocol/steem_operations.cpp index 87bceb6d20..4ea066feec 100644 --- a/libraries/protocol/steem_operations.cpp +++ b/libraries/protocol/steem_operations.cpp @@ -256,6 +256,10 @@ namespace golos { namespace protocol { GOLOS_CHECK_VALUE_LE(max_referral_interest_rate, GOLOS_MAX_REFERRAL_INTEREST_RATE); GOLOS_CHECK_VALUE_LE(max_referral_term_sec, GOLOS_MAX_REFERRAL_TERM_SEC); GOLOS_CHECK_VALUE_LEGE(max_referral_break_fee.amount, 0, GOLOS_MAX_REFERRAL_BREAK_FEE.amount); + GOLOS_CHECK_VALUE_LEGE(comments_window, 1, std::numeric_limits::max() / 2); + GOLOS_CHECK_VALUE_LEGE(comments_per_window, 1, comments_window); + GOLOS_CHECK_VALUE_LEGE(votes_window, 1, std::numeric_limits::max() / 2); + GOLOS_CHECK_VALUE_LEGE(votes_per_window, 1, votes_window); } void witness_update_operation::validate() const { diff --git a/libraries/wallet/include/golos/wallet/wallet.hpp b/libraries/wallet/include/golos/wallet/wallet.hpp index 4168f84317..b6dc6a939b 100644 --- a/libraries/wallet/include/golos/wallet/wallet.hpp +++ b/libraries/wallet/include/golos/wallet/wallet.hpp @@ -48,8 +48,13 @@ namespace golos { namespace wallet { fc::optional max_referral_interest_rate; fc::optional max_referral_term_sec; fc::optional max_referral_break_fee; - fc::optional auction_window_size; + fc::optional comments_window; + fc::optional comments_per_window; + fc::optional votes_window; + fc::optional votes_per_window; + + fc::optional auction_window_size; }; struct optional_private_box_query { @@ -1524,7 +1529,8 @@ FC_REFLECT((golos::wallet::optional_chain_props), (account_creation_fee)(maximum_block_size)(sbd_interest_rate) (create_account_min_golos_fee)(create_account_min_delegation) (create_account_delegation_time)(min_delegation) - (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee)(auction_window_size)) + (max_referral_interest_rate)(max_referral_term_sec)(max_referral_break_fee) + (comments_window)(comments_per_window)(votes_window)(votes_per_window)(auction_window_size)) FC_REFLECT( (golos::wallet::message_body), diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index accf711612..4c48cb12f8 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -327,10 +327,14 @@ namespace golos { namespace wallet { result["min_delegation"] = median_props.min_delegation; } if (hf >= hardfork_version(0, STEEMIT_HARDFORK_0_19)) { - result["auction_window_size"] = median_props.auction_window_size; result["max_referral_interest_rate"] = median_props.max_referral_interest_rate; result["max_referral_term_sec"] = median_props.max_referral_term_sec; result["max_referral_break_fee"] = median_props.max_referral_break_fee; + result["comments_window"] = median_props.comments_window; + result["comments_per_window"] = median_props.comments_per_window; + result["votes_window"] = median_props.votes_window; + result["votes_per_window"] = median_props.votes_per_window; + result["auction_window_size"] = median_props.auction_window_size; } return result; @@ -2254,12 +2258,18 @@ fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_st op.props = p; auto hf = my->_remote_database_api->get_hardfork_version(); if (hf >= hardfork_version(0, STEEMIT_HARDFORK_0_19) || !!props.max_referral_interest_rate - || !!props.max_referral_term_sec || !!props.max_referral_break_fee) { + || !!props.max_referral_term_sec || !!props.max_referral_break_fee || !!props.comments_window + || !!props.comments_per_window || !!props.votes_window || !!props.votes_per_window + || !!props.auction_window_size) { chain_properties_19 p19; p19 = p; SET_PROP(p19, max_referral_interest_rate); SET_PROP(p19, max_referral_term_sec); SET_PROP(p19, max_referral_break_fee); + SET_PROP(p19, comments_window); + SET_PROP(p19, comments_per_window); + SET_PROP(p19, votes_window); + SET_PROP(p19, votes_per_window); SET_PROP(p19, auction_window_size); op.props = p19; }