Scorum
scorum_evaluators.cpp
Go to the documentation of this file.
2 
3 #include <scorum/rewards_math/curve.hpp>
4 
21 
23 
24 #include <scorum/rewards_math/formulas.hpp>
25 
30 
31 #ifndef IS_LOW_MEM
32 #include <diff_match_patch.h>
33 #include <boost/locale/encoding_utf.hpp>
34 
35 using boost::locale::conv::utf_to_utf;
36 
37 std::wstring utf8_to_wstring(const std::string& str)
38 {
39  return utf_to_utf<wchar_t>(str.c_str(), str.c_str() + str.size());
40 }
41 
42 std::string wstring_to_utf8(const std::wstring& str)
43 {
44  return utf_to_utf<char>(str.c_str(), str.c_str() + str.size());
45 }
46 
47 #endif
48 
49 #include <fc/uint128.hpp>
50 #include <fc/utf8.hpp>
51 
52 #include <limits>
53 
54 namespace scorum {
55 namespace chain {
56 
57 inline void validate_permlink_0_1(const std::string& permlink)
58 {
59  FC_ASSERT(permlink.size() > SCORUM_MIN_PERMLINK_LENGTH && permlink.size() < SCORUM_MAX_PERMLINK_LENGTH,
60  "Permlink is not a valid size.");
61 
62  for (auto ch : permlink)
63  {
64  if (!std::islower(ch) && !std::isdigit(ch) && !(ch == '-'))
65  {
66  FC_ASSERT(false, "Invalid permlink character: ${ch}", ("ch", std::string(1, ch)));
67  }
68  }
69 }
70 
72 {
73  bool operator()(const fc::shared_string& a, const std::string& b)
74  {
75  return a.size() == b.size() && std::strcmp(a.c_str(), b.c_str()) == 0;
76  }
77 };
78 
79 void witness_update_evaluator::do_apply(const witness_update_operation& o)
80 {
81  account_service_i& account_service = db().account_service();
82  witness_service_i& witness_service = db().witness_service();
83 
84  account_service.check_account_existence(o.owner);
85 
86  FC_ASSERT(o.url.size() <= SCORUM_MAX_WITNESS_URL_LENGTH, "URL is too long");
87  FC_ASSERT(o.proposed_chain_props.account_creation_fee.symbol() == SCORUM_SYMBOL);
88 
89  if (!witness_service.is_exists(o.owner))
90  {
91  witness_service.create_witness(o.owner, o.url, o.block_signing_key, o.proposed_chain_props);
92  }
93  else
94  {
95  const auto& witness = witness_service.get(o.owner);
96  witness_service.update_witness(witness, o.url, o.block_signing_key, o.proposed_chain_props);
97  }
98 }
99 
100 void account_create_evaluator::do_apply(const account_create_operation& o)
101 {
102  account_service_i& account_service = db().account_service();
103  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
104 
105  const auto& creator = account_service.get_account(o.creator);
106 
107  // check creator balance
108 
109  FC_ASSERT(creator.balance >= o.fee, "Insufficient balance to create account.",
110  ("creator.balance", creator.balance)("required", o.fee));
111 
112  // check fee
113 
114  const auto creation_fee
115  = dprops_service.get().median_chain_props.account_creation_fee * SCORUM_CREATE_ACCOUNT_WITH_SCORUM_MODIFIER;
116  FC_ASSERT(o.fee >= creation_fee, "Insufficient Fee: ${f} required, ${p} provided.",
117  ("f", creation_fee)("p", o.fee));
118 
119  // check accounts existence
120 
121  account_service.check_account_existence(o.owner.account_auths);
122 
123  account_service.check_account_existence(o.active.account_auths);
124 
125  account_service.check_account_existence(o.posting.account_auths);
126 
127  // write in to DB
128 
129  account_service.create_account(o.new_account_name, o.creator, o.memo_key, o.json_metadata, o.owner, o.active,
130  o.posting, o.fee);
131 }
132 
133 void account_create_with_delegation_evaluator::do_apply(const account_create_with_delegation_operation& o)
134 {
135  account_service_i& account_service = db().account_service();
136  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
137  withdraw_scorumpower_service_i& withdraw_scorumpower_service = db().withdraw_scorumpower_service();
138 
139  const auto& creator = account_service.get_account(o.creator);
140 
141  // check creator balance
142 
143  FC_ASSERT(creator.balance >= o.fee, "Insufficient balance to create account.",
144  ("creator.balance", creator.balance)("required", o.fee));
145 
146  // check delegation fee
147 
148  asset withdraw_rest = withdraw_scorumpower_service.get_withdraw_rest(creator.id);
149 
150  FC_ASSERT(creator.scorumpower - creator.delegated_scorumpower - withdraw_rest >= o.delegation,
151  "Insufficient scorumpower to delegate to new account.",
152  ("creator.scorumpower", creator.scorumpower)("creator.delegated_scorumpower",
153  creator.delegated_scorumpower)("required", o.delegation));
154 
155  const auto median_creation_fee = dprops_service.get().median_chain_props.account_creation_fee;
156 
157  auto target_delegation = asset(median_creation_fee.amount * SCORUM_CREATE_ACCOUNT_WITH_SCORUM_MODIFIER
159  SP_SYMBOL);
160 
161  auto current_delegation = asset(o.fee.amount * SCORUM_CREATE_ACCOUNT_DELEGATION_RATIO, SP_SYMBOL) + o.delegation;
162 
163  FC_ASSERT(current_delegation >= target_delegation, "Inssufficient Delegation ${f} required, ${p} provided.",
164  ("f", target_delegation)("p", current_delegation)("account_creation_fee", median_creation_fee)(
165  "o.fee", o.fee)("o.delegation", o.delegation));
166 
167  FC_ASSERT(o.fee >= median_creation_fee, "Insufficient Fee: ${f} required, ${p} provided.",
168  ("f", median_creation_fee)("p", o.fee));
169 
170  // check accounts existence
171 
172  account_service.check_account_existence(o.owner.account_auths);
173 
174  account_service.check_account_existence(o.active.account_auths);
175 
176  account_service.check_account_existence(o.posting.account_auths);
177 
178  account_service.create_account_with_delegation(o.new_account_name, o.creator, o.memo_key, o.json_metadata, o.owner,
179  o.active, o.posting, o.fee, o.delegation);
180 }
181 
182 void account_update_evaluator::do_apply(const account_update_operation& o)
183 {
184  if (o.posting)
185  o.posting->validate();
186 
187  account_service_i& account_service = db().account_service();
188 
189  const auto& account = account_service.get_account(o.account);
190  const auto& account_auth = account_service.get_account_authority(o.account);
191 
192  if (o.owner)
193  {
194  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
195 
196  FC_ASSERT(dprops_service.head_block_time() - account_auth.last_owner_update > SCORUM_OWNER_UPDATE_LIMIT,
197  "Owner authority can only be updated once an hour.");
198 
199  account_service.check_account_existence(o.owner->account_auths);
200 
201  account_service.update_owner_authority(account, *o.owner);
202  }
203 
204  if (o.active)
205  {
206  account_service.check_account_existence(o.active->account_auths);
207  }
208 
209  if (o.posting)
210  {
211  account_service.check_account_existence(o.posting->account_auths);
212  }
213 
214  account_service.update_acount(account, account_auth, o.memo_key, o.json_metadata, o.owner, o.active, o.posting);
215 }
216 
220 void delete_comment_evaluator::do_apply(const delete_comment_operation& o)
221 {
222  account_service_i& account_service = db().account_service();
223  comment_service_i& comment_service = db().comment_service();
224  comment_vote_service_i& comment_vote_service = db().comment_vote_service();
225  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
226 
227  const auto& auth = account_service.get_account(o.author);
228  FC_ASSERT(!(auth.owner_challenged || auth.active_challenged),
229  "Operation cannot be processed because account is currently challenged.");
230 
231  const auto& comment = comment_service.get(o.author, o.permlink);
232  FC_ASSERT(comment.children == 0, "Cannot delete a comment with replies.");
233 
234  FC_ASSERT(comment.cashout_time != fc::time_point_sec::maximum());
235 
236  FC_ASSERT(comment.net_rshares <= 0, "Cannot delete a comment with net positive votes.");
237 
238  if (comment.net_rshares > 0)
239  return;
240 
241  comment_vote_service.remove_by_comment(comment_id_type(comment.id));
242 
243  account_name_type parent_author = comment.parent_author;
244  std::string parent_permlink = fc::to_string(comment.parent_permlink);
246  while (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
247  {
248  const comment_object& parent = comment_service.get(parent_author, parent_permlink);
249 
250  parent_author = parent.parent_author;
251  parent_permlink = fc::to_string(parent.parent_permlink);
252 
253  auto now = dprops_service.head_block_time();
254 
255  comment_service.update(parent, [&](comment_object& p) {
256  p.children--;
257  p.active = now;
258  });
259 
260 #ifdef IS_LOW_MEM
261  break;
262 #endif
263  }
264 
265  comment_service.remove(comment);
266 }
267 
269 {
270  comment_options_extension_visitor(const comment_object& c, data_service_factory_i& db)
271  : _c(c)
272  , _account_service(db.account_service())
273  , _comment_service(db.comment_service())
274  {
275  }
276 
278  {
279  FC_ASSERT(_c.beneficiaries.size() == 0, "Comment already has beneficiaries specified.");
280  FC_ASSERT(_c.abs_rshares == 0, "Comment must not have been voted on before specifying beneficiaries.");
281 
282  _comment_service.update(_c, [&](comment_object& c) {
283  for (auto& b : cpb.beneficiaries)
284  {
285  _account_service.check_account_existence(b.account, "Beneficiary");
286  c.beneficiaries.push_back(b);
287  }
288  });
289  }
290 
291  const comment_object& _c;
294 };
295 
296 void comment_options_evaluator::do_apply(const comment_options_operation& o)
297 {
298  account_service_i& account_service = db().account_service();
299  comment_service_i& comment_service = db().comment_service();
300 
301  const auto& auth = account_service.get_account(o.author);
302  FC_ASSERT(!(auth.owner_challenged || auth.active_challenged),
303  "Operation cannot be processed because account is currently challenged.");
304 
305  const auto& comment = comment_service.get(o.author, o.permlink);
306  if (!o.allow_curation_rewards || !o.allow_votes || o.max_accepted_payout < comment.max_accepted_payout)
307  FC_ASSERT(comment.abs_rshares == 0,
308  "One of the included comment options requires the comment to have no rshares allocated to it.");
309 
310  FC_ASSERT(comment.allow_curation_rewards >= o.allow_curation_rewards, "Curation rewards cannot be re-enabled.");
311  FC_ASSERT(comment.allow_votes >= o.allow_votes, "Voting cannot be re-enabled.");
312  FC_ASSERT(comment.max_accepted_payout >= o.max_accepted_payout, "A comment cannot accept a greater payout.");
313 
314  comment_service.update(comment, [&](comment_object& c) {
315  c.max_accepted_payout = o.max_accepted_payout;
316  c.allow_votes = o.allow_votes;
317  c.allow_curation_rewards = o.allow_curation_rewards;
318  });
319 
320  for (auto& e : o.extensions)
321  {
322  e.visit(comment_options_extension_visitor(comment, db()));
323  }
324 }
325 
326 void comment_evaluator::do_apply(const comment_operation& o)
327 {
328  account_service_i& account_service = db().account_service();
329  comment_service_i& comment_service = db().comment_service();
330  comment_statistic_scr_service_i& comment_statistic_scr_service = db().comment_statistic_scr_service();
331  comment_statistic_sp_service_i& comment_statistic_sp_service = db().comment_statistic_sp_service();
332  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
333 
334  try
335  {
336 
337  FC_ASSERT(o.title.size() + o.body.size() + o.json_metadata.size(),
338  "Cannot update comment because nothing appears to be changing.");
339 
340  const auto& auth = account_service.get_account(o.author);
341 
342  FC_ASSERT(!(auth.owner_challenged || auth.active_challenged),
343  "Operation cannot be processed because account is currently challenged.");
344 
345  account_name_type parent_author = o.parent_author;
346  std::string parent_permlink = o.parent_permlink;
347  if (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
348  {
349  const comment_object& parent = comment_service.get(parent_author, parent_permlink);
350  FC_ASSERT(parent.depth < SCORUM_MAX_COMMENT_DEPTH,
351  "Comment is nested ${x} posts deep, maximum depth is ${y}.",
352  ("x", parent.depth)("y", SCORUM_MAX_COMMENT_DEPTH));
353  }
354 
355  auto now = dprops_service.head_block_time();
356 
357  if (!comment_service.is_exists(o.author, o.permlink))
358  {
359  if (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
360  {
361  const comment_object& parent = comment_service.get(parent_author, parent_permlink);
362  FC_ASSERT(comment_service.get(parent.root_comment).allow_replies,
363  "The parent comment has disabled replies.");
364  }
365 
366  if (parent_author == SCORUM_ROOT_POST_PARENT_ACCOUNT)
367  FC_ASSERT((now - auth.last_root_post) > SCORUM_MIN_ROOT_COMMENT_INTERVAL,
368  "You may only post once every 5 minutes.",
369  ("now", now)("last_root_post", auth.last_root_post));
370  else
371  FC_ASSERT((now - auth.last_post) > SCORUM_MIN_REPLY_INTERVAL,
372  "You may only comment once every 20 seconds.",
373  ("now", now)("auth.last_post", auth.last_post));
374 
375  account_service.add_post(auth, parent_author);
376 
377  validate_permlink_0_1(parent_permlink);
378  validate_permlink_0_1(o.permlink);
379 
380  account_name_type pr_parent_author;
381  std::string pr_parent_permlink;
382  uint16_t pr_depth = 0;
383  std::string pr_category;
384  comment_id_type pr_root_comment;
385  if (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
386  {
387  const comment_object& parent = comment_service.get(parent_author, parent_permlink);
388  pr_parent_author = parent.author;
389  pr_parent_permlink = fc::to_string(parent.permlink);
390  pr_depth = parent.depth;
391  pr_category = fc::to_string(parent.category);
392  pr_root_comment = parent.root_comment;
393  }
394 
395  const comment_object& new_comment = comment_service.create([&](comment_object& com) {
396 
397  com.author = o.author;
398  fc::from_string(com.permlink, o.permlink);
399  com.last_update = now;
400  com.created = com.last_update;
401  com.active = com.last_update;
402  com.last_payout = fc::time_point_sec();
403 
404  if (parent_author == SCORUM_ROOT_POST_PARENT_ACCOUNT)
405  {
406  com.parent_author = "";
407  fc::from_string(com.parent_permlink, parent_permlink);
408  fc::from_string(com.category, parent_permlink);
409  com.root_comment = com.id;
410  }
411  else
412  {
413  com.parent_author = pr_parent_author;
414  fc::from_string(com.parent_permlink, pr_parent_permlink);
415  com.depth = pr_depth + 1;
416  fc::from_string(com.category, pr_category);
417  com.root_comment = pr_root_comment;
418  }
419 
420  com.cashout_time = com.created + SCORUM_CASHOUT_WINDOW_SECONDS;
421 
422 #ifndef IS_LOW_MEM
423  fc::from_string(com.title, o.title);
424  if (o.body.size() < 1024 * 1024 * 128)
425  {
426  fc::from_string(com.body, o.body);
427  }
428 
429  fc::from_string(com.json_metadata, o.json_metadata);
430 #endif
431  });
432 
433  comment_statistic_scr_service.create(
434  [&](comment_statistic_scr_object& stat) { stat.comment = new_comment.id; });
435  comment_statistic_sp_service.create(
436  [&](comment_statistic_sp_object& stat) { stat.comment = new_comment.id; });
437 
438 #ifndef IS_LOW_MEM
439  {
440  account_blogging_statistic_service_i& account_blogging_statistic_service
441  = db().account_blogging_statistic_service();
442 
443  const auto& author_stat = account_blogging_statistic_service.obtain(auth.id);
444  account_blogging_statistic_service.add_post(author_stat);
445  if (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
446  {
447  account_blogging_statistic_service.add_comment(author_stat);
448  }
449  }
450 #endif
452  while (parent_author != SCORUM_ROOT_POST_PARENT_ACCOUNT)
453  {
454  const comment_object& parent = comment_service.get(parent_author, parent_permlink);
455 
456  parent_author = parent.parent_author;
457  parent_permlink = fc::to_string(parent.parent_permlink);
458 
459  comment_service.update(parent, [&](comment_object& p) {
460  p.children++;
461  p.active = now;
462  });
463 #ifdef IS_LOW_MEM
464  break;
465 #endif
466  }
467  }
468  else // start edit case
469  {
470  const comment_object& comment = comment_service.get(o.author, o.permlink);
471 
472  comment_service.update(comment, [&](comment_object& com) {
473  com.last_update = dprops_service.head_block_time();
474  com.active = com.last_update;
475  strcmp_equal equal;
476 
477  if (parent_author == SCORUM_ROOT_POST_PARENT_ACCOUNT)
478  {
479  FC_ASSERT(com.parent_author == account_name_type(), "The parent of a comment cannot be changed.");
480  FC_ASSERT(equal(com.parent_permlink, parent_permlink), "The permlink of a comment cannot change.");
481  }
482  else
483  {
484  FC_ASSERT(com.parent_author == o.parent_author, "The parent of a comment cannot be changed.");
485  FC_ASSERT(equal(com.parent_permlink, parent_permlink), "The permlink of a comment cannot change.");
486  }
487 
488 #ifndef IS_LOW_MEM
489  if (o.title.size())
490  fc::from_string(com.title, o.title);
491  if (!o.json_metadata.empty())
492  {
493  fc::from_string(com.json_metadata, o.json_metadata);
494  }
495 
496  if (!o.body.empty())
497  {
498  try
499  {
500  diff_match_patch<std::wstring> dmp;
501  auto patch = dmp.patch_fromText(utf8_to_wstring(o.body));
502  if (patch.size())
503  {
504  auto result = dmp.patch_apply(patch, utf8_to_wstring(fc::to_string(com.body)));
505  auto patched_body = wstring_to_utf8(result.first);
506  if (!fc::is_utf8(patched_body))
507  {
508  idump(("invalid utf8")(patched_body));
509  fc::from_string(com.body, fc::prune_invalid_utf8(patched_body));
510  }
511  else
512  {
513  fc::from_string(com.body, patched_body);
514  }
515  }
516  else
517  { // replace
518  fc::from_string(com.body, o.body);
519  }
520  }
521  catch (...)
522  {
523  fc::from_string(com.body, o.body);
524  }
525  }
526 #endif
527  });
528 
529  } // end EDIT case
530  }
531  FC_CAPTURE_AND_RETHROW((o))
532 }
533 
534 void escrow_transfer_evaluator::do_apply(const escrow_transfer_operation& o)
535 {
536  account_service_i& account_service = db().account_service();
537  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
538  escrow_service_i& escrow_service = db().escrow_service();
539 
540  try
541  {
542  const auto& from_account = account_service.get_account(o.from);
543  account_service.check_account_existence(o.to);
544  account_service.check_account_existence(o.agent);
545 
546  FC_ASSERT(o.ratification_deadline > dprops_service.head_block_time(),
547  "The escorw ratification deadline must be after head block time.");
548  FC_ASSERT(o.escrow_expiration > dprops_service.head_block_time(),
549  "The escrow expiration must be after head block time.");
550 
551  FC_ASSERT(o.fee.symbol() == SCORUM_SYMBOL, "Fee must be in SCR.");
552 
553  asset scorum_spent = o.scorum_amount;
554  scorum_spent += o.fee;
555 
556  FC_ASSERT(from_account.balance >= scorum_spent,
557  "Account cannot cover SCR costs of escrow. Required: ${r} Available: ${a}",
558  ("r", scorum_spent)("a", from_account.balance));
559 
560  account_service.decrease_balance(from_account, scorum_spent);
561 
562  escrow_service.create_escrow(o.escrow_id, o.from, o.to, o.agent, o.ratification_deadline, o.escrow_expiration,
563  o.scorum_amount, o.fee);
564  }
565  FC_CAPTURE_AND_RETHROW((o))
566 }
567 
568 void escrow_approve_evaluator::do_apply(const escrow_approve_operation& o)
569 {
570  account_service_i& account_service = db().account_service();
571  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
572  escrow_service_i& escrow_service = db().escrow_service();
573 
574  try
575  {
576 
577  const auto& escrow = escrow_service.get(o.from, o.escrow_id);
578 
579  FC_ASSERT(escrow.to == o.to, "Operation 'to' (${o}) does not match escrow 'to' (${e}).",
580  ("o", o.to)("e", escrow.to));
581  FC_ASSERT(escrow.agent == o.agent, "Operation 'agent' (${a}) does not match escrow 'agent' (${e}).",
582  ("o", o.agent)("e", escrow.agent));
583  FC_ASSERT(escrow.ratification_deadline >= dprops_service.head_block_time(),
584  "The escrow ratification deadline has passed. Escrow can no longer be ratified.");
585 
586  bool reject_escrow = !o.approve;
587 
588  if (o.who == o.to)
589  {
590  FC_ASSERT(!escrow.to_approved, "Account 'to' (${t}) has already approved the escrow.", ("t", o.to));
591 
592  if (!reject_escrow)
593  {
594  escrow_service.update(escrow, [&](escrow_object& esc) { esc.to_approved = true; });
595  }
596  }
597  if (o.who == o.agent)
598  {
599  FC_ASSERT(!escrow.agent_approved, "Account 'agent' (${a}) has already approved the escrow.",
600  ("a", o.agent));
601 
602  if (!reject_escrow)
603  {
604  escrow_service.update(escrow, [&](escrow_object& esc) { esc.agent_approved = true; });
605  }
606  }
607 
608  if (reject_escrow)
609  {
610  const auto& from_account = account_service.get_account(o.from);
611  account_service.increase_balance(from_account, escrow.scorum_balance);
612  account_service.increase_balance(from_account, escrow.pending_fee);
613 
614  escrow_service.remove(escrow);
615  }
616  else if (escrow.to_approved && escrow.agent_approved)
617  {
618  const auto& agent_account = account_service.get_account(o.agent);
619  account_service.increase_balance(agent_account, escrow.pending_fee);
620 
621  escrow_service.update(escrow, [&](escrow_object& esc) { esc.pending_fee.amount = 0; });
622  }
623  }
624  FC_CAPTURE_AND_RETHROW((o))
625 }
626 
627 void escrow_dispute_evaluator::do_apply(const escrow_dispute_operation& o)
628 {
629  account_service_i& account_service = db().account_service();
630  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
631  escrow_service_i& escrow_service = db().escrow_service();
632 
633  try
634  {
635  account_service.check_account_existence(o.from);
636 
637  const auto& e = escrow_service.get(o.from, o.escrow_id);
638  FC_ASSERT(dprops_service.head_block_time() < e.escrow_expiration,
639  "Disputing the escrow must happen before expiration.");
640  FC_ASSERT(e.to_approved && e.agent_approved,
641  "The escrow must be approved by all parties before a dispute can be raised.");
642  FC_ASSERT(!e.disputed, "The escrow is already under dispute.");
643  FC_ASSERT(e.to == o.to, "Operation 'to' (${o}) does not match escrow 'to' (${e}).", ("o", o.to)("e", e.to));
644  FC_ASSERT(e.agent == o.agent, "Operation 'agent' (${a}) does not match escrow 'agent' (${e}).",
645  ("o", o.agent)("e", e.agent));
646 
647  escrow_service.update(e, [&](escrow_object& esc) { esc.disputed = true; });
648  }
649  FC_CAPTURE_AND_RETHROW((o))
650 }
651 
652 void escrow_release_evaluator::do_apply(const escrow_release_operation& o)
653 {
654  account_service_i& account_service = db().account_service();
655  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
656  escrow_service_i& escrow_service = db().escrow_service();
657 
658  try
659  {
660  account_service.check_account_existence(o.from);
661  const auto& receiver_account = account_service.get_account(o.receiver);
662 
663  const auto& e = escrow_service.get(o.from, o.escrow_id);
664  FC_ASSERT(e.scorum_balance >= o.scorum_amount,
665  "Release amount exceeds escrow balance. Amount: ${a}, Balance: ${b}",
666  ("a", o.scorum_amount)("b", e.scorum_balance));
667  FC_ASSERT(e.to == o.to, "Operation 'to' (${o}) does not match escrow 'to' (${e}).", ("o", o.to)("e", e.to));
668  FC_ASSERT(e.agent == o.agent, "Operation 'agent' (${a}) does not match escrow 'agent' (${e}).",
669  ("o", o.agent)("e", e.agent));
670  FC_ASSERT(o.receiver == e.from || o.receiver == e.to, "Funds must be released to 'from' (${f}) or 'to' (${t})",
671  ("f", e.from)("t", e.to));
672  FC_ASSERT(e.to_approved && e.agent_approved, "Funds cannot be released prior to escrow approval.");
673 
674  // If there is a dispute regardless of expiration, the agent can release funds to either party
675  if (e.disputed)
676  {
677  FC_ASSERT(o.who == e.agent, "Only 'agent' (${a}) can release funds in a disputed escrow.", ("a", e.agent));
678  }
679  else
680  {
681  FC_ASSERT(o.who == e.from || o.who == e.to,
682  "Only 'from' (${f}) and 'to' (${t}) can release funds from a non-disputed escrow",
683  ("f", e.from)("t", e.to));
684 
685  if (e.escrow_expiration > dprops_service.head_block_time())
686  {
687  // If there is no dispute and escrow has not expired, either party can release funds to the other.
688  if (o.who == e.from)
689  {
690  FC_ASSERT(o.receiver == e.to, "Only 'from' (${f}) can release funds to 'to' (${t}).",
691  ("f", e.from)("t", e.to));
692  }
693  else if (o.who == e.to)
694  {
695  FC_ASSERT(o.receiver == e.from, "Only 'to' (${t}) can release funds to 'from' (${t}).",
696  ("f", e.from)("t", e.to));
697  }
698  }
699  }
700  // If escrow expires and there is no dispute, either party can release funds to either party.
701 
702  account_service.increase_balance(receiver_account, o.scorum_amount);
703 
704  escrow_service.update(e, [&](escrow_object& esc) { esc.scorum_balance -= o.scorum_amount; });
705 
706  if (e.scorum_balance.amount == 0)
707  {
708  escrow_service.remove(e);
709  }
710  }
711  FC_CAPTURE_AND_RETHROW((o))
712 }
713 
714 void transfer_evaluator::do_apply(const transfer_operation& o)
715 {
716  account_service_i& account_service = db().account_service();
717 
718  const auto& from_account = account_service.get_account(o.from);
719  const auto& to_account = account_service.get_account(o.to);
720 
721  account_service.drop_challenged(from_account);
722 
723  FC_ASSERT(from_account.balance >= o.amount, "Account does not have sufficient funds for transfer.");
724  account_service.decrease_balance(from_account, o.amount);
725  account_service.increase_balance(to_account, o.amount);
726 }
727 
728 void transfer_to_scorumpower_evaluator::do_apply(const transfer_to_scorumpower_operation& o)
729 {
730  account_service_i& account_service = db().account_service();
731 
732  const auto& from_account = account_service.get_account(o.from);
733  const auto& to_account = o.to.size() ? account_service.get_account(o.to) : from_account;
734 
735  FC_ASSERT(from_account.balance >= o.amount, "Account does not have sufficient SCR for transfer.");
736  account_service.decrease_balance(from_account, o.amount);
737  account_service.create_scorumpower(to_account, o.amount);
738 }
739 
740 void account_witness_proxy_evaluator::do_apply(const account_witness_proxy_operation& o)
741 {
742  account_service_i& account_service = db().account_service();
743 
744  const auto& account = account_service.get_account(o.account);
745  FC_ASSERT(account.proxy != o.proxy, "Proxy must change.");
746 
747  FC_ASSERT(account.can_vote, "Account has declined the ability to vote and cannot proxy votes.");
748 
749  optional<account_object> proxy;
750  if (o.proxy.size())
751  {
752  proxy = account_service.get_account(o.proxy);
753  }
754  account_service.update_voting_proxy(account, proxy);
755 }
756 
757 void account_witness_vote_evaluator::do_apply(const account_witness_vote_operation& o)
758 {
759  account_service_i& account_service = db().account_service();
760  witness_service_i& witness_service = db().witness_service();
761  witness_vote_service_i& witness_vote_service = db().witness_vote_service();
762 
763  const auto& voter = account_service.get_account(o.account);
764  FC_ASSERT(voter.proxy.size() == 0, "A proxy is currently set, please clear the proxy before voting for a witness.");
765 
766  if (o.approve)
767  FC_ASSERT(voter.can_vote, "Account has declined its voting rights.");
768 
769  const auto& witness = witness_service.get(o.witness);
770 
771  if (!witness_vote_service.is_exists(witness.id, voter.id))
772  {
773  FC_ASSERT(o.approve, "Vote doesn't exist, user must indicate a desire to approve witness.");
774 
775  FC_ASSERT(voter.witnesses_voted_for < SCORUM_MAX_ACCOUNT_WITNESS_VOTES,
776  "Account has voted for too many witnesses."); // TODO: Remove after hardfork 2
777 
778  witness_vote_service.create([&](witness_vote_object& v) {
779  v.witness = witness.id;
780  v.account = voter.id;
781  });
782 
783  witness_service.adjust_witness_vote(witness, voter.witness_vote_weight());
784 
785  account_service.increase_witnesses_voted_for(voter);
786  }
787  else
788  {
789  FC_ASSERT(!o.approve, "Vote currently exists, user must indicate a desire to reject witness.");
790 
791  witness_service.adjust_witness_vote(witness, -voter.witness_vote_weight());
792 
793  account_service.decrease_witnesses_voted_for(voter);
794 
795  const witness_vote_object& witness_vote_object = witness_vote_service.get(witness.id, voter.id);
796  witness_vote_service.remove(witness_vote_object);
797  }
798 }
799 
800 void prove_authority_evaluator::do_apply(const prove_authority_operation& o)
801 {
802  account_service_i& account_service = db().account_service();
803 
804  const auto& challenged = account_service.get_account(o.challenged);
805  FC_ASSERT(challenged.owner_challenged || challenged.active_challenged,
806  "Account is not challeneged. No need to prove authority.");
807 
808  account_service.prove_authority(challenged, o.require_owner);
809 }
810 
811 void request_account_recovery_evaluator::do_apply(const request_account_recovery_operation& o)
812 {
813  account_service_i& account_service = db().account_service();
814  witness_service_i& witness_service = db().witness_service();
815 
816  const auto& account_to_recover = account_service.get_account(o.account_to_recover);
817 
818  if (account_to_recover.recovery_account.length()) // Make sure recovery matches expected recovery account
819  FC_ASSERT(account_to_recover.recovery_account == o.recovery_account,
820  "Cannot recover an account that does not have you as there recovery partner.");
821  else // Empty string recovery account defaults to top witness
822  FC_ASSERT(witness_service.get_top_witness().owner == o.recovery_account,
823  "Top witness must recover an account with no recovery partner.");
824 
825  account_service.create_account_recovery(o.account_to_recover, o.new_owner_authority);
826 }
827 
828 void recover_account_evaluator::do_apply(const recover_account_operation& o)
829 {
830  account_service_i& account_service = db().account_service();
831  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
832 
833  const auto& account_to_recover = account_service.get_account(o.account_to_recover);
834 
835  FC_ASSERT(dprops_service.head_block_time() - account_to_recover.last_account_recovery > SCORUM_OWNER_UPDATE_LIMIT,
836  "Owner authority can only be updated once an hour.");
837 
838  account_service.submit_account_recovery(account_to_recover, o.new_owner_authority, o.recent_owner_authority);
839 }
840 
841 void change_recovery_account_evaluator::do_apply(const change_recovery_account_operation& o)
842 {
843  account_service_i& account_service = db().account_service();
844 
845  account_service.check_account_existence(o.new_recovery_account); // Simply validate account exists
846  const auto& account_to_recover = account_service.get_account(o.account_to_recover);
847 
848  account_service.change_recovery_account(account_to_recover, o.new_recovery_account);
849 }
850 
851 void decline_voting_rights_evaluator::do_apply(const decline_voting_rights_operation& o)
852 {
853  account_service_i& account_service = db().account_service();
854  decline_voting_rights_request_service_i& dvrr_service = db().decline_voting_rights_request_service();
855 
856  const auto& account = account_service.get_account(o.account);
857 
858  if (o.decline)
859  {
860  FC_ASSERT(!dvrr_service.is_exists(account.id), "Cannot create new request because one already exists.");
861 
862  dvrr_service.create_rights(account.id, SCORUM_OWNER_AUTH_RECOVERY_PERIOD);
863  }
864  else
865  {
866  const auto& request = dvrr_service.get(account.id);
867 
868  dvrr_service.remove(request);
869  }
870 }
871 
872 void delegate_scorumpower_evaluator::do_apply(const delegate_scorumpower_operation& op)
873 {
874  account_service_i& account_service = db().account_service();
875  scorumpower_delegation_service_i& sp_delegation_service = db().scorumpower_delegation_service();
876  dynamic_global_property_service_i& dprops_service = db().dynamic_global_property_service();
877  withdraw_scorumpower_service_i& withdraw_scorumpower_service = db().withdraw_scorumpower_service();
878 
879  const auto& delegator = account_service.get_account(op.delegator);
880  const auto& delegatee = account_service.get_account(op.delegatee);
881 
882  auto available_shares = delegator.scorumpower - delegator.delegated_scorumpower
883  - withdraw_scorumpower_service.get_withdraw_rest(delegator.id);
884 
885  const auto& dprops = dprops_service.get();
886  auto min_delegation = asset(
887  dprops.median_chain_props.account_creation_fee.amount * SCORUM_MIN_DELEGATE_VESTING_SHARES_MODIFIER, SP_SYMBOL);
888  auto min_update = asset(dprops.median_chain_props.account_creation_fee.amount, SP_SYMBOL);
889 
890  // If delegation doesn't exist, create it
891  if (!sp_delegation_service.is_exists(op.delegator, op.delegatee))
892  {
893  FC_ASSERT(available_shares >= op.scorumpower, "Account does not have enough scorumpower to delegate.");
894  FC_ASSERT(op.scorumpower >= min_delegation, "Account must delegate a minimum of ${v}", ("v", min_delegation));
895 
896  sp_delegation_service.create([&](scorumpower_delegation_object& spdo) {
897  spdo.delegator = op.delegator;
898  spdo.delegatee = op.delegatee;
899  spdo.scorumpower = op.scorumpower;
900  spdo.min_delegation_time = dprops.time;
901  });
902 
903  account_service.increase_delegated_scorumpower(delegator, op.scorumpower);
904  account_service.increase_received_scorumpower(delegatee, op.scorumpower);
905  }
906  else
907  {
908  const auto& delegation = sp_delegation_service.get(op.delegator, op.delegatee);
909 
910  // Else if the delegation is increasing
911  if (op.scorumpower >= delegation.scorumpower)
912  {
913  auto delta = op.scorumpower - delegation.scorumpower;
914 
915  FC_ASSERT(delta >= min_update, "Scorum Power increase is not enough of a difference. min_update: ${min}",
916  ("min", min_update));
917  FC_ASSERT(available_shares >= op.scorumpower - delegation.scorumpower,
918  "Account does not have enough scorumpower to delegate.");
919 
920  account_service.increase_delegated_scorumpower(delegator, delta);
921  account_service.increase_received_scorumpower(delegatee, delta);
922 
923  sp_delegation_service.update(delegation,
924  [&](scorumpower_delegation_object& obj) { obj.scorumpower = op.scorumpower; });
925  }
926  // Else the delegation is decreasing
927  else /* delegation.scorumpower > op.scorumpower */
928  {
929  auto delta = delegation.scorumpower - op.scorumpower;
930 
931  if (op.scorumpower.amount > 0)
932  {
933  FC_ASSERT(delta >= min_update,
934  "Scorum Power decrease is not enough of a difference. min_update: ${min}",
935  ("min", min_update));
936  FC_ASSERT(op.scorumpower >= min_delegation,
937  "Delegation must be removed or leave minimum delegation amount of ${v}",
938  ("v", min_delegation));
939  }
940  else
941  {
942  FC_ASSERT(delegation.scorumpower.amount > 0,
943  "Delegation would set scorumpower to zero, but it is already zero");
944  }
945 
946  sp_delegation_service.create_expiration(
947  op.delegator, delta, std::max(dprops_service.head_block_time() + SCORUM_CASHOUT_WINDOW_SECONDS,
948  delegation.min_delegation_time));
949 
950  account_service.decrease_received_scorumpower(delegatee, delta);
951 
952  if (op.scorumpower.amount > 0)
953  {
954  sp_delegation_service.update(
955  delegation, [&](scorumpower_delegation_object& obj) { obj.scorumpower = op.scorumpower; });
956  }
957  else
958  {
959  sp_delegation_service.remove(delegation);
960  }
961  }
962  }
963 }
964 
965 void atomicswap_initiate_evaluator::do_apply(const atomicswap_initiate_operation& op)
966 {
967  atomicswap_service_i& atomicswap_service = db().atomicswap_service();
968  account_service_i& account_service = db().account_service();
969 
970  account_service.check_account_existence(op.owner);
971  account_service.check_account_existence(op.recipient);
972 
973  const auto& owner = account_service.get_account(op.owner);
974  const auto& recipient = account_service.get_account(op.recipient);
975 
976  optional<std::string> metadata;
977  if (!op.metadata.empty())
978  {
979  metadata = op.metadata;
980  }
981 
982  switch (op.type)
983  {
985  atomicswap_service.create_contract(atomicswap_contract_initiator, owner, recipient, op.amount, op.secret_hash,
986  metadata);
987  break;
989  atomicswap_service.create_contract(atomicswap_contract_participant, owner, recipient, op.amount, op.secret_hash,
990  metadata);
991  break;
992  default:
993  FC_ASSERT(false, "Invalid operation type.");
994  }
995 }
996 
997 void atomicswap_redeem_evaluator::do_apply(const atomicswap_redeem_operation& op)
998 {
999  atomicswap_service_i& atomicswap_service = db().atomicswap_service();
1000  account_service_i& account_service = db().account_service();
1001 
1002  account_service.check_account_existence(op.from);
1003  account_service.check_account_existence(op.to);
1004 
1005  const auto& from = account_service.get_account(op.from);
1006  const auto& to = account_service.get_account(op.to);
1007 
1008  std::string secret_hash = atomicswap::get_secret_hash(op.secret);
1009 
1010  const auto& contract = atomicswap_service.get_contract(from, to, secret_hash);
1011 
1012  atomicswap_service.redeem_contract(contract, op.secret);
1013 }
1014 
1015 void atomicswap_refund_evaluator::do_apply(const atomicswap_refund_operation& op)
1016 {
1017  atomicswap_service_i& atomicswap_service = db().atomicswap_service();
1018  account_service_i& account_service = db().account_service();
1019 
1020  account_service.check_account_existence(op.participant);
1021  account_service.check_account_existence(op.initiator);
1022 
1023  const auto& from = account_service.get_account(op.participant);
1024  const auto& to = account_service.get_account(op.initiator);
1025 
1026  const auto& contract = atomicswap_service.get_contract(from, to, op.secret_hash);
1027 
1028  FC_ASSERT(contract.type != atomicswap_contract_initiator,
1029  "Can't refund initiator contract. It is locked on ${h} hours.",
1031 
1032  atomicswap_service.refund_contract(contract);
1033 }
1034 
1035 } // namespace chain
1036 } // namespace scorum
#define SCORUM_OWNER_AUTH_RECOVERY_PERIOD
Definition: config.hpp:138
#define SCORUM_CREATE_ACCOUNT_WITH_SCORUM_MODIFIER
Definition: config.hpp:120
#define SCORUM_ATOMICSWAP_INITIATOR_REFUND_LOCK_SECS
Definition: config.hpp:149
#define SCORUM_MAX_COMMENT_DEPTH
Definition: config.hpp:215
#define SCORUM_MAX_ACCOUNT_WITNESS_VOTES
Definition: config.hpp:198
#define SCORUM_OWNER_UPDATE_LIMIT
Definition: config.hpp:140
#define SP_SYMBOL
Definition: config.hpp:104
#define SCORUM_SYMBOL
Definition: config.hpp:102
#define SCORUM_CASHOUT_WINDOW_SECONDS
Definition: config.hpp:134
#define SCORUM_MIN_ROOT_COMMENT_INTERVAL
Definition: config.hpp:195
#define SCORUM_MIN_REPLY_INTERVAL
Definition: config.hpp:196
#define SCORUM_MIN_DELEGATE_VESTING_SHARES_MODIFIER
Definition: config.hpp:122
#define SCORUM_CREATE_ACCOUNT_DELEGATION_RATIO
Definition: config.hpp:220
oid< comment_object > comment_id_type
void validate_permlink_0_1(const std::string &permlink)
std::string get_secret_hash(const std::string &secret_hex)
fc::fixed_string_16 account_name_type
Definition: types.hpp:62
Definition: asset.cpp:15
std::string wstring_to_utf8(const std::wstring &str)
std::wstring utf8_to_wstring(const std::string &str)
virtual const account_object & get_account(const account_name_type &) const =0
virtual void update(const modifier_type &modifier)=0
comment_options_extension_visitor(const comment_object &c, data_service_factory_i &db)
void operator()(const comment_payout_beneficiaries &cpb) const
virtual const comment_object & get(const comment_id_type &comment_id) const =0
bool operator()(const fc::shared_string &a, const std::string &b)
bool allow_curation_rewards
allows a post to receive votes;
comment_options_extensions_type extensions
allows voters to receive curation rewards. Rewards return to reward fund.
bool allow_votes
SCR value of the maximum payout this post will receive.
std::vector< beneficiary_route_type > beneficiaries
Definition: comment.hpp:31