Scorum
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
comments_cashout_impl.cpp
Go to the documentation of this file.
2 #include <boost/range/combine.hpp>
3 
4 namespace scorum {
5 namespace chain {
6 namespace database_ns {
7 
9  : _ctx(ctx)
10  , dgp_service(ctx.services().dynamic_global_property_service())
11  , account_service(ctx.services().account_service())
12  , account_blogging_statistic_service(ctx.services().account_blogging_statistic_service())
13  , comment_service(ctx.services().comment_service())
14  , comment_statistic_scr_service(ctx.services().comment_statistic_scr_service())
15  , comment_statistic_sp_service(ctx.services().comment_statistic_sp_service())
16  , comment_vote_service(ctx.services().comment_vote_service())
17  , hardfork_service(ctx.services().hardfork_property_service())
18 {
19 }
20 
21 void process_comments_cashout_impl::close_comment_payout(const comment_object& comment)
22 {
23  comment_service.update(comment, [&](comment_object& c) {
24 
25  // TODO: remove abs_* fields (it is used only in tests)
26  c.children_abs_rshares = 0;
27  c.abs_rshares = 0;
28 
29  // to reset next payout for curators
30  c.total_vote_weight = 0;
31 
32  c.cashout_time = fc::time_point_sec::maximum();
33  c.last_payout = dgp_service.head_block_time();
34  });
35 
36  _ctx.push_virtual_operation(comment_payout_update_operation(comment.author, fc::to_string(comment.permlink)));
37 
38 #ifdef CLEAR_VOTES
39  auto comment_votes = comment_vote_service.get_by_comment(comment.id);
40  for (const comment_vote_object& vote : comment_votes)
41  {
42  comment_vote_service.remove(vote);
43  }
44 #endif
45 }
46 
47 fc::uint128_t process_comments_cashout_impl::get_total_claims(const comment_refs_type& comments,
48  curve_id reward_curve,
49  fc::uint128_t recent_claims) const
50 {
51  shares_vector_type total_rshares;
52  for (const comment_object& comment : comments)
53  {
54  total_rshares.push_back(comment.net_rshares);
55  }
56 
57  fc::uint128_t total_claims = rewards_math::calculate_total_claims(recent_claims, reward_curve, total_rshares);
58 
59  return total_claims;
60 }
61 
62 template <typename TFundService>
63 void process_comments_cashout_impl::reward(TFundService& fund_service, const comment_refs_type& comments)
64 {
65  const auto& fund = fund_service.get();
66  if (fund.activity_reward_balance.amount < 1 || comments.empty())
67  return;
68 
69  auto total_claims = get_total_claims(comments, fund.author_reward_curve, fund.recent_claims);
70 
71  auto fund_rewards
72  = calculate_comments_payout(comments, fund.activity_reward_balance, total_claims, fund.author_reward_curve);
73 
74  auto total_reward = hardfork_service.has_hardfork(SCORUM_HARDFORK_0_3)
75  ? pay_for_comments(comments, fund_rewards)
76  : pay_for_comments_legacy(comments, fund_rewards);
77 
78  fund_service.update([&](typename TFundService::object_type& rfo) {
79  rfo.recent_claims = total_claims;
80  rfo.activity_reward_balance -= total_reward;
81  rfo.last_update = dgp_service.head_block_time();
82  });
83 }
84 
86  const std::vector<asset>& fund_rewards)
87 {
88  FC_ASSERT(comments.size() == fund_rewards.size(), "comments count and comments' rewards count should be equal");
89  FC_ASSERT(!fund_rewards.empty(), "collection cannot be empty");
90 
91  auto reward_symbol = fund_rewards[0].symbol();
92  auto total_claimed_reward = asset(0, reward_symbol);
93 
94  for (const auto& item : boost::combine(comments, fund_rewards))
95  {
96  const auto& comment = item.get<0>().get();
97  const auto& fund_reward = item.get<1>();
98 
99  auto rewards = pay_curators(comment, fund_reward);
100  auto beneficiaries_reward = pay_beneficiaries(comment, rewards.author_reward);
101 
102  auto curators_reward = rewards.curators_reward;
103  auto author_reward = rewards.author_reward - beneficiaries_reward;
104 
105  const auto& author = account_service.get_account(comment.author);
106  pay_account(author, author_reward);
107 
108  auto claimed_reward = curators_reward + author_reward + beneficiaries_reward;
109  total_claimed_reward += claimed_reward;
110 
111  comment_service.update(comment, [&](comment_object& c) { c.last_payout = dgp_service.head_block_time(); });
112  if (author_reward.amount > 0)
113  comment_service.set_rewarded_flag(comment);
114 
116  author_reward_operation(comment.author, fc::to_string(comment.permlink), author_reward));
117 
118  // clang-format off
120  comment.author,
121  fc::to_string(comment.permlink),
122  fund_reward,
123  claimed_reward,
124  author_reward,
125  curators_reward,
126  asset(0, reward_symbol),
127  asset(0, reward_symbol),
128  beneficiaries_reward));
129 
130  accumulate_statistic(comment,
131  author,
132  fund_reward,
133  author_reward,
134  curators_reward,
135  asset(0, reward_symbol),
136  asset(0, reward_symbol),
137  beneficiaries_reward,
138  reward_symbol);
139  // clang-format on
140  }
141 
142  return total_claimed_reward;
143 }
144 
146  const std::vector<asset>& fund_rewards)
147 {
148  FC_ASSERT(comments.size() == fund_rewards.size(), "comments count and comments' rewards count should be equal");
149  FC_ASSERT(fund_rewards.size() > 0, "collection cannot be empty");
150 
151  asset_symbol_type reward_symbol = fund_rewards[0].symbol();
152 
153  struct comment_reward
154  {
155  share_type fund; // reward accrued from fund
156  share_type commenting; // reward accrued from children comments
157  };
158  struct comment_key
159  {
160  account_name_type author;
161  fc::shared_string permlink;
162  };
163  auto less = [](const comment_key& l, const comment_key& r) {
164  return std::tie(l.author, l.permlink) < std::tie(r.author, r.permlink);
165  };
166 
167  std::map<comment_key, comment_reward, decltype(less)> comment_rewards(less);
168  for (auto i = 0u; i < comments.size(); ++i)
169  {
170  comment_rewards.emplace(comment_key{ comments[i].get().author, get_permlink(comments[i].get().permlink) },
171  comment_reward{ fund_rewards[i].amount, 0 });
172  }
173 
174  asset total_reward = asset(0, reward_symbol);
175 
176  // newest, with bigger depth comments first
177  comment_refs_type comments_with_parents = collect_parents(comments);
178 
179  for (const comment_object& comment : comments_with_parents)
180  {
181  const auto& reward = comment_rewards[comment_key{ comment.author, get_permlink(comment.permlink) }];
182 
183  asset fund_reward = asset(reward.fund, reward_symbol);
184  asset parent_payout_value = asset(reward.commenting, reward_symbol);
185 
186  comment_payout_result payout_result = pay_for_comment_legacy(comment, fund_reward, parent_payout_value);
187 
188  total_reward += payout_result.total_claimed_reward;
189 
190  // save payout for the parent comment
191  comment_rewards[comment_key{ comment.parent_author, get_permlink(comment.parent_permlink) }].commenting
192  += payout_result.parent_comment_reward.amount;
193  }
194 
195  return total_reward;
196 }
197 
199  const comment_object& comment, const asset& fund_reward, const asset& payout_from_children)
200 {
201  try
202  {
203  auto reward_symbol = fund_reward.symbol();
204  comment_payout_result payout_result{ asset(0, reward_symbol), asset(0, reward_symbol) };
205  if (fund_reward.amount < 1 && payout_from_children.amount < 1)
206  return payout_result;
207 
208  auto rewards = pay_curators(comment, fund_reward);
209  auto author_reward = rewards.author_reward;
210  auto curators_reward = rewards.curators_reward;
211 
212  auto payout_to_parent = asset(0, reward_symbol);
213  if (comment.depth != 0)
214  {
215  auto fraction = utils::make_fraction(SCORUM_PARENT_COMMENT_REWARD_PERCENT, SCORUM_100_PERCENT);
216  payout_to_parent = (author_reward + payout_from_children) * fraction;
217  }
218 
219  author_reward = (author_reward + payout_from_children) - payout_to_parent;
220 
221  auto beneficiaries_reward = pay_beneficiaries(comment, author_reward);
222  author_reward -= beneficiaries_reward;
223 
224  const auto& author = account_service.get_account(comment.author);
225  pay_account(author, author_reward);
226 
227  payout_result.total_claimed_reward = curators_reward + author_reward + beneficiaries_reward;
228  payout_result.parent_comment_reward = payout_to_parent;
229 
230  comment_service.update(comment, [&](comment_object& c) { c.last_payout = dgp_service.head_block_time(); });
231  if (author_reward.amount > 0 || payout_from_children.amount > 0)
232  comment_service.set_rewarded_flag(comment);
233 
235  author_reward_operation(comment.author, fc::to_string(comment.permlink), author_reward));
236 
237  // clang-format off
239  comment.author,
240  fc::to_string(comment.permlink),
241  fund_reward,
242  payout_result.total_claimed_reward,
243  author_reward,
244  curators_reward,
245  payout_from_children,
246  payout_to_parent,
247  beneficiaries_reward));
248 
249  accumulate_statistic(comment,
250  author,
251  fund_reward,
252  author_reward,
253  curators_reward,
254  payout_from_children,
255  payout_to_parent,
256  beneficiaries_reward,
257  reward_symbol);
258  // clang-format on
259 
260  return payout_result;
261  }
262  FC_CAPTURE_AND_RETHROW((comment))
263 }
264 
266 process_comments_cashout_impl::pay_curators(const comment_object& comment, const asset& fund_reward)
267 {
268  try
269  {
270  auto reward_symbol = fund_reward.symbol();
271  auto potential_reward = fund_reward * utils::make_fraction(SCORUM_CURATION_REWARD_PERCENT, SCORUM_100_PERCENT);
272 
273  if (!comment.allow_curation_rewards)
274  return { asset(0, reward_symbol), fund_reward - potential_reward };
275  else if (comment.total_vote_weight <= 0)
276  return { asset(0, reward_symbol), fund_reward };
277 
278  auto rewarded = asset(0, reward_symbol);
279  auto comment_votes = comment_vote_service.get_by_comment_weight_voter(comment.id);
280  for (const comment_vote_object& vote : comment_votes)
281  {
282  auto claim = potential_reward * utils::make_fraction(vote.weight, comment.total_vote_weight);
283  if (claim.amount > 0)
284  {
285  rewarded += claim;
286 
287  const auto& voter = account_service.get(vote.voter);
288  pay_account(voter, claim);
289 
291  curation_reward_operation(voter.name, claim, comment.author, fc::to_string(comment.permlink)));
292 
293  accumulate_statistic(voter, claim);
294  }
295  }
296 
297  return { rewarded, fund_reward - rewarded };
298  }
299  FC_CAPTURE_AND_RETHROW((comment.author)(comment.permlink)(fund_reward))
300 }
301 
302 asset process_comments_cashout_impl::pay_beneficiaries(const comment_object& comment, const asset& author_reward)
303 {
304  auto reward_symbol = author_reward.symbol();
305 
306  auto beneficiaries_reward = asset(0, reward_symbol);
307  for (auto& beneficiary : comment.beneficiaries)
308  {
309  auto beneficiary_reward = author_reward * utils::make_fraction(beneficiary.weight, SCORUM_100_PERCENT);
310  pay_account(account_service.get_account(beneficiary.account), beneficiary_reward);
311  beneficiaries_reward += beneficiary_reward;
312 
313  _ctx.push_virtual_operation(comment_benefficiary_reward_operation(
314  beneficiary.account, comment.author, fc::to_string(comment.permlink), beneficiary_reward));
315  }
316 
317  return beneficiaries_reward;
318 }
319 
320 void process_comments_cashout_impl::pay_account(const account_object& recipient, const asset& reward)
321 {
322  if (SCORUM_SYMBOL == reward.symbol())
323  {
324  account_service.increase_balance(recipient, reward);
325  }
326  else if (SP_SYMBOL == reward.symbol())
327  {
328  account_service.create_scorumpower(recipient, reward);
329  }
330 }
331 
332 std::vector<asset> process_comments_cashout_impl::calculate_comments_payout(const comment_refs_type& comments,
333  const asset& reward_fund_balance,
334  fc::uint128_t total_claims,
335  curve_id reward_curve) const
336 {
337  std::vector<asset> rewards;
338  rewards.reserve(comments.size());
339 
340  for (const comment_object& comment : comments)
341  {
342  share_type payout = rewards_math::calculate_payout(
343  comment.net_rshares, total_claims, reward_fund_balance.amount, reward_curve,
344  comment.max_accepted_payout.amount, SCORUM_MIN_COMMENT_PAYOUT_SHARE);
345 
346  rewards.emplace_back(payout, reward_fund_balance.symbol());
347  }
348 
349  return rewards;
350 }
351 
352 void process_comments_cashout_impl::accumulate_statistic(const comment_object& comment,
353  const account_object& author,
354  const asset& fund_reward,
355  const asset& author_payout,
356  const asset& curation_payout,
357  const asset& payout_from_children,
358  const asset& payout_to_parent,
359  const asset& beneficiary_payout,
360  asset_symbol_type reward_symbol)
361 {
362  FC_ASSERT(author_payout.symbol() == reward_symbol);
363  FC_ASSERT(curation_payout.symbol() == reward_symbol);
364  FC_ASSERT(beneficiary_payout.symbol() == reward_symbol);
365 
366  asset total_payout = author_payout;
367  total_payout += curation_payout;
368  total_payout += beneficiary_payout;
369 
370  // clang-format off
371  if (SCORUM_SYMBOL == reward_symbol)
372  {
373  accumulate_comment_statistic(comment_statistic_scr_service, comment,
374  fund_reward,
375  total_payout,
376  author_payout,
377  curation_payout,
378  payout_from_children,
379  payout_to_parent,
380  beneficiary_payout);
381  }
382  else if (SP_SYMBOL == reward_symbol)
383  {
384  accumulate_comment_statistic(comment_statistic_sp_service, comment,
385  fund_reward,
386  total_payout,
387  author_payout,
388  curation_payout,
389  payout_from_children,
390  payout_to_parent,
391  beneficiary_payout);
392  }
393 // clang-format on
394 
395 #ifndef IS_LOW_MEM
396  {
397  const auto& author_stat = account_blogging_statistic_service.obtain(author.id);
398  account_blogging_statistic_service.increase_posting_rewards(author_stat, author_payout);
399  }
400 #endif
401 }
402 
403 void process_comments_cashout_impl::accumulate_statistic(const account_object& voter, const asset& curation_payout)
404 {
405 #ifndef IS_LOW_MEM
406  const auto& voter_stat = account_blogging_statistic_service.obtain(voter.id);
407  account_blogging_statistic_service.increase_curation_rewards(voter_stat, curation_payout);
408 #endif
409 }
410 
411 comment_refs_type process_comments_cashout_impl::collect_parents(const comment_refs_type& comments)
412 {
413  struct by_depth_greater
414  {
415  bool operator()(const comment_object& lhs, const comment_object& rhs)
416  {
417  return std::tie(lhs.depth, lhs.id) > std::tie(rhs.depth, rhs.id);
418  }
419  };
420 
421  using comment_refs_set = std::set<comment_refs_type::value_type, by_depth_greater>;
422  comment_refs_set ordered_comments(comments.begin(), comments.end());
423 
424  // 'ordered_comments' set is sorted by depth in desc order.
425  for (auto it = ordered_comments.begin(); it != ordered_comments.end() && it->get().depth != 0; ++it)
426  {
427  const comment_object& comment = it->get();
428 
429  const auto& parent_comment = comment_service.get(comment.parent_author, fc::to_string(comment.parent_permlink));
430 
431  // insert parent if it doesn't exist or do nothing if exists. Insertable parent will be always 'after' current
432  // comment because of the set ordering
433  ordered_comments.insert(parent_comment);
434  }
435 
436  comment_refs_type comments_with_parents(ordered_comments.begin(), ordered_comments.end());
437 
438  return comments_with_parents;
439 }
440 
441 fc::shared_string process_comments_cashout_impl::get_permlink(const fc::shared_string& str) const
442 {
443  if (hardfork_service.has_hardfork(SCORUM_HARDFORK_0_1))
444  return str;
445  else
446  return fc::shared_string("", str.get_allocator());
447 }
448 
449 // Explicit template instantiation
450 // clang-format off
451 template void process_comments_cashout_impl::reward<content_reward_fund_scr_service_i>(content_reward_fund_scr_service_i&, const comment_refs_type&);
452 template void process_comments_cashout_impl::reward<content_reward_fund_sp_service_i>(content_reward_fund_sp_service_i&, const comment_refs_type&);
453 template void process_comments_cashout_impl::reward<content_fifa_world_cup_2018_bounty_reward_fund_service_i>( content_fifa_world_cup_2018_bounty_reward_fund_service_i&, const comment_refs_type&);
454 // clang-format on
455 }
456 }
457 }
virtual void push_virtual_operation(const operation &op)
Definition: block_tasks.cpp:22
comment_payout_result pay_for_comment_legacy(const comment_object &comment, const asset &publication_reward, const asset &parent_payout_value)
asset pay_for_comments(const comment_refs_type &comments, const std::vector< asset > &fund_rewards)
asset pay_for_comments_legacy(const comment_refs_type &comments, const std::vector< asset > &fund_rewards)
These two methods distribute comments reward across all parent comments.
void reward(TFundService &fund_service, const comment_refs_type &comments)
#define SP_SYMBOL
Definition: config.hpp:104
#define SCORUM_SYMBOL
Definition: config.hpp:102
#define SCORUM_CURATION_REWARD_PERCENT
Definition: config.hpp:208
#define SCORUM_PARENT_COMMENT_REWARD_PERCENT
Definition: config.hpp:209
#define SCORUM_100_PERCENT
Definition: config.hpp:200
#define SCORUM_MIN_COMMENT_PAYOUT_SHARE
Definition: config.hpp:116
scorum::chain::comment_service_i::comment_refs_type comment_refs_type
fc::safe< share_value_type > share_type
Definition: types.hpp:73
fc::fixed_string_16 account_name_type
Definition: types.hpp:62
uint64_t asset_symbol_type
Definition: asset.hpp:13
Definition: asset.cpp:15
virtual void increase_curation_rewards(const account_blogging_statistic_object &stat, const asset &amount)=0
virtual void increase_posting_rewards(const account_blogging_statistic_object &stat, const asset &amount)=0
virtual const account_blogging_statistic_object & obtain(const account_id_type &account_id)=0
virtual const asset create_scorumpower(const account_object &to_account, const asset &scorum)=0
virtual const account_object & get_account(const account_name_type &) const =0
virtual void increase_balance(const account_object &account, const asset &amount)=0
virtual const account_object & get(const account_id_type &) const =0
virtual void update(const modifier_type &modifier)=0
virtual const comment_object & get(const comment_id_type &comment_id) const =0
virtual void set_rewarded_flag(const comment_object &comment)=0
virtual comment_vote_refs_type get_by_comment_weight_voter(const comment_id_type &comment_id) const =0
virtual comment_vote_refs_type get_by_comment(const comment_id_type &comment_id) const =0
asset total_claimed_reward
amount of tokens distributed within particular comment across author, beneficiars and curators
virtual fc::time_point_sec head_block_time() const =0
virtual bool has_hardfork(uint32_t hardfork) const =0
asset_symbol_type symbol() const
Definition: asset.hpp:32
share_type amount
Definition: asset.hpp:31