Scorum
vote_evaluator.cpp
Go to the documentation of this file.
2 
4 
12 
13 #include <scorum/rewards_math/formulas.hpp>
14 
15 namespace scorum {
16 namespace chain {
17 
18 vote_evaluator::vote_evaluator(data_service_factory_i& services)
19  : evaluator_impl<data_service_factory_i, vote_evaluator>(services)
20  , _account_service(services.account_service())
21  , _comment_service(services.comment_service())
22  , _comment_vote_service(services.comment_vote_service())
23  , _dgp_service(services.dynamic_global_property_service())
24  , _hardfork_service(services.hardfork_property_service())
25 {
26 }
27 
29 {
30  if (_hardfork_service.has_hardfork(SCORUM_HARDFORK_0_2))
31  {
32  FC_ASSERT(abs(o.weight) <= SCORUM_100_PERCENT, "Weight is not a SCORUM percentage");
33  return o.weight;
34  }
35  else
36  {
37  FC_ASSERT(abs(o.weight) <= 100, "Weight is not a SCORUM percentage");
38  return static_cast<vote_weight_type>(o.weight * SCORUM_1_PERCENT);
39  }
40 }
41 
43 {
44  try
45  {
46  const auto& comment = _comment_service.get(o.author, o.permlink);
47  const auto& voter = _account_service.get_account(o.voter);
48 
49  FC_ASSERT(!(voter.owner_challenged || voter.active_challenged),
50  "Operation cannot be processed because the account is currently challenged.");
51 
52  FC_ASSERT(voter.can_vote, "Voter has declined their voting rights.");
53 
54  const vote_weight_type weight = get_weigth(o);
55 
56  if (weight > 0)
57  FC_ASSERT(comment.allow_votes, "Votes are not allowed on the comment.");
58 
59  if (comment.cashout_time == fc::time_point_sec::maximum())
60  {
61 #ifndef CLEAR_VOTES
62  if (!_comment_vote_service.is_exists(comment.id, voter.id))
63  {
64  _comment_vote_service.create([&](comment_vote_object& cvo) {
65  cvo.voter = voter.id;
66  cvo.comment = comment.id;
67  cvo.vote_percent = weight;
68  cvo.last_update = _dgp_service.head_block_time();
69  });
70  }
71  else
72  {
73  const comment_vote_object& comment_vote = _comment_vote_service.get(comment.id, voter.id);
74  _comment_vote_service.update(comment_vote, [&](comment_vote_object& cvo) {
75  cvo.vote_percent = weight;
76  cvo.last_update = _dgp_service.head_block_time();
77  });
78  }
79 #endif
80  return;
81  }
82 
83  {
84  int64_t elapsed_seconds = (_dgp_service.head_block_time() - voter.last_vote_time).to_seconds();
85  FC_ASSERT(elapsed_seconds >= SCORUM_MIN_VOTE_INTERVAL_SEC, "Can only vote once every 3 seconds.");
86  }
87 
88  uint16_t current_power = rewards_math::calculate_restoring_power(
89  voter.voting_power, _dgp_service.head_block_time(), voter.last_vote_time, SCORUM_VOTE_REGENERATION_SECONDS);
90  FC_ASSERT(current_power > 0, "Account currently does not have voting power.");
91 
92  uint16_t used_power
93  = rewards_math::calculate_used_power(current_power, weight, SCORUM_VOTING_POWER_DECAY_PERCENT);
94 
95  FC_ASSERT(used_power <= current_power, "Account does not have enough power to vote.");
96 
97  share_type abs_rshares
98  = rewards_math::calculate_abs_reward_shares(used_power, voter.effective_scorumpower().amount);
99 
100  FC_ASSERT(abs_rshares > SCORUM_VOTE_DUST_THRESHOLD || weight == 0,
101  "Voting weight is too small, please accumulate more voting power or scorum power.");
102 
104  share_type rshares = weight < 0 ? -abs_rshares : abs_rshares;
105 
106  if (!_comment_vote_service.is_exists(comment.id, voter.id))
107  {
108  FC_ASSERT(weight != 0, "Vote weight cannot be 0.");
109 
110  if (rshares > 0)
111  {
112  FC_ASSERT(_dgp_service.head_block_time() < comment.cashout_time - SCORUM_UPVOTE_LOCKOUT,
113  "Cannot increase payout within last twelve hours before payout.");
114  }
115 
116  _account_service.update_voting_power(voter, current_power - used_power);
117 
118  const auto& root_comment = _comment_service.get(comment.root_comment);
119 
120  FC_ASSERT(abs_rshares > 0, "Cannot vote with 0 rshares.");
121 
122  auto old_vote_rshares = comment.vote_rshares;
123 
124  _comment_service.update(comment, [&](comment_object& c) {
125  c.net_rshares += rshares;
126  c.abs_rshares += abs_rshares;
127  if (rshares > 0)
128  c.vote_rshares += rshares;
129  if (rshares > 0)
130  c.net_votes++;
131  else
132  c.net_votes--;
133  });
134 
135  _comment_service.update(root_comment, [&](comment_object& c) { c.children_abs_rshares += abs_rshares; });
136 
137  uint64_t max_vote_weight = 0;
138 
139  _comment_vote_service.create([&](comment_vote_object& cv) {
140  cv.voter = voter.id;
141  cv.comment = comment.id;
142  cv.rshares = rshares;
143  cv.vote_percent = weight;
144  cv.last_update = _dgp_service.head_block_time();
145 
146  bool curation_reward_eligible
147  = rshares > 0 && (comment.last_payout == fc::time_point_sec()) && comment.allow_curation_rewards;
148 
149  if (curation_reward_eligible)
150  {
151  const auto& reward_fund = db().content_reward_fund_scr_service().get();
152  max_vote_weight = rewards_math::calculate_max_vote_weight(comment.vote_rshares, old_vote_rshares,
153  reward_fund.curation_reward_curve);
154  cv.weight = rewards_math::calculate_vote_weight(max_vote_weight, cv.last_update, comment.created,
156  }
157  else
158  {
159  cv.weight = 0;
160  }
161  });
162 
163 #ifndef IS_LOW_MEM
164  {
165  account_blogging_statistic_service_i& account_blogging_statistic_service
166  = db().account_blogging_statistic_service();
167 
168  const auto& voter_stat = account_blogging_statistic_service.obtain(voter.id);
169  account_blogging_statistic_service.add_vote(voter_stat);
170  }
171 #endif
172 
173  if (max_vote_weight) // Optimization
174  {
175  _comment_service.update(comment, [&](comment_object& c) { c.total_vote_weight += max_vote_weight; });
176  }
177  }
178  else
179  {
180  const comment_vote_object& comment_vote = _comment_vote_service.get(comment.id, voter.id);
181 
182  FC_ASSERT(comment_vote.num_changes != -1, "Cannot vote again on a comment after payout.");
183 
184  FC_ASSERT(comment_vote.num_changes < SCORUM_MAX_VOTE_CHANGES,
185  "Voter has used the maximum number of vote changes on this comment.");
186 
187  FC_ASSERT(comment_vote.vote_percent != weight, "You have already voted in a similar way.");
188 
189  if (comment_vote.rshares < rshares)
190  {
191  FC_ASSERT(_dgp_service.head_block_time() < comment.cashout_time - SCORUM_UPVOTE_LOCKOUT,
192  "Cannot increase payout within last twelve hours before payout.");
193  }
194 
195  _account_service.update_voting_power(voter, current_power - used_power);
196 
197  const auto& root_comment = _comment_service.get(comment.root_comment);
198 
199  _comment_service.update(comment, [&](comment_object& c) {
200  c.net_rshares -= comment_vote.rshares;
201  c.net_rshares += rshares;
202  c.abs_rshares += abs_rshares;
203 
205  if (rshares > 0 && comment_vote.rshares < 0)
206  c.net_votes += 2;
207  else if (rshares > 0 && comment_vote.rshares == 0)
208  c.net_votes += 1;
209  else if (rshares == 0 && comment_vote.rshares < 0)
210  c.net_votes += 1;
211  else if (rshares == 0 && comment_vote.rshares > 0)
212  c.net_votes -= 1;
213  else if (rshares < 0 && comment_vote.rshares == 0)
214  c.net_votes -= 1;
215  else if (rshares < 0 && comment_vote.rshares > 0)
216  c.net_votes -= 2;
217  });
218 
219  _comment_service.update(root_comment, [&](comment_object& c) { c.children_abs_rshares += abs_rshares; });
220 
221  _comment_service.update(comment, [&](comment_object& c) { c.total_vote_weight -= comment_vote.weight; });
222 
223  _comment_vote_service.update(comment_vote, [&](comment_vote_object& cv) {
224  cv.rshares = rshares;
225  cv.vote_percent = weight;
226  cv.last_update = _dgp_service.head_block_time();
227  cv.weight = 0;
228  cv.num_changes += 1;
229  });
230  }
231  }
232  FC_CAPTURE_AND_RETHROW()
233 }
234 
235 } // namespace chain
236 } // namespace scorum
vote_evaluator(data_service_factory_i &services)
void do_apply(const operation_type &op)
protocol::vote_weight_type get_weigth(const operation_type &o) const
#define SCORUM_MIN_VOTE_INTERVAL_SEC
Definition: config.hpp:158
#define SCORUM_VOTE_REGENERATION_SECONDS
Definition: config.hpp:191
#define SCORUM_1_PERCENT
Definition: config.hpp:201
#define SCORUM_UPVOTE_LOCKOUT
Definition: config.hpp:136
#define SCORUM_100_PERCENT
Definition: config.hpp:200
#define SCORUM_MAX_VOTE_CHANGES
Definition: config.hpp:192
#define SCORUM_REVERSE_AUCTION_WINDOW_SECONDS
Definition: config.hpp:193
#define SCORUM_VOTE_DUST_THRESHOLD
Definition: config.hpp:108
fc::safe< share_value_type > share_type
Definition: types.hpp:73
int16_t vote_weight_type
Definition: types.hpp:70
Definition: asset.cpp:15
virtual const account_blogging_statistic_object & obtain(const account_id_type &account_id)=0
virtual void add_vote(const account_blogging_statistic_object &stat)=0
virtual const account_object & get_account(const account_name_type &) const =0
virtual void update_voting_power(const account_object &account, uint16_t voting_power)=0
virtual void update(const modifier_type &modifier)=0
virtual const object_type & create(const modifier_type &modifier)=0
virtual const comment_object & get(const comment_id_type &comment_id) const =0
virtual const comment_vote_object & get(const comment_id_type &comment_id, const account_id_type &voter_id) const =0
virtual bool is_exists(const comment_id_type &comment_id, const account_id_type &voter_id) const =0
virtual fc::time_point_sec head_block_time() const =0
virtual bool has_hardfork(uint32_t hardfork) const =0