Scorum
process_funds.cpp
Go to the documentation of this file.
3 
13 
19 
21 
23 #include <boost/range/adaptor/transformed.hpp>
24 #include <boost/range/algorithm/transform.hpp>
25 #include <boost/range/algorithm_ext/copy_n.hpp>
26 #include <scorum/utils/take_n_range.hpp>
27 #include <scorum/utils/collect_range_adaptor.hpp>
28 
29 #include <vector>
30 
31 namespace scorum {
32 namespace chain {
33 namespace database_ns {
34 
36 
38  : _content_reward_service(ctx.services().content_reward_scr_service())
39  , _dev_pool_service(ctx.services().dev_pool_service())
40  , _dprops_service(ctx.services().dynamic_global_property_service())
41  , _fund_budget_service(ctx.services().fund_budget_service())
42  , _post_budget_service(ctx.services().post_budget_service())
43  , _banner_budget_service(ctx.services().banner_budget_service())
44  , _account_service(ctx.services().account_service())
45  , _adv_property_svc(ctx.services().advertising_property_service())
46  , _virt_op_emitter(ctx)
47  , _voter_reward_scr_svc(ctx.services().voters_reward_scr_service())
48  , _voter_reward_sp_svc(ctx.services().voters_reward_sp_service())
49  , _content_reward_fund_scr_svc(ctx.services().content_reward_fund_scr_service())
50  , _content_reward_fund_sp_svc(ctx.services().content_reward_fund_sp_service())
51  , _hardfork_svc(ctx.services().hardfork_property_service())
52  , _witness_svc(ctx.services().witness_service())
53  , _ctx(ctx)
54  , _block_num(ctx.block_num())
55 {
56 }
57 
59 {
60  if (apply_mainnet_schedule_crutches())
61  return;
62 
63  debug_log(ctx.get_block_info(), "process_funds BEGIN");
64 
65  // We don't have inflation.
66  // We just get per block reward from original reward fund(4.8M SP)
67  // and expect that after initial supply is handed out(fund budget is over) reward budgets will be created by our
68  // users(through the purchase of advertising). Advertising budgets are in SCR.
69 
70  asset original_fund_reward = asset(0, SP_SYMBOL);
71  if (_fund_budget_service.is_exists())
72  {
73  original_fund_reward += _fund_budget_service.allocate_cash(_fund_budget_service.get());
74  }
75  distribute_reward(original_fund_reward); // distribute SP
76 
77  auto adv_budgets_reward = run_auction_round();
78 
79  // 50% of the revenue goes to support and develop the product, namely,
80  // towards the company's R&D center.
81  auto dev_team_reward_factor = utils::make_fraction(SCORUM_DEV_TEAM_PER_BLOCK_REWARD_PERCENT, SCORUM_100_PERCENT);
82  auto dev_team_reward = adv_budgets_reward * dev_team_reward_factor;
83 
84  _dev_pool_service.update([&](dev_committee_object& dco) { dco.scr_balance += dev_team_reward; });
85 
86  // 50% of revenue is distributed in SCR among users.
87  // pass it through reward balancer
88  reward_balance_algorithm<content_reward_scr_service_i> balancer(_content_reward_service);
89  balancer.increase_ballance(adv_budgets_reward - dev_team_reward);
90  asset users_reward = balancer.take_block_reward();
91 
92  distribute_reward(users_reward); // distribute SCR
93 
94  debug_log(ctx.get_block_info(), "process_funds END");
95 }
96 
97 asset process_funds::run_auction_round()
98 {
99  advertising_auction auction(_dprops_service, _adv_property_svc, _post_budget_service, _banner_budget_service);
100  auction.run_round();
101 
102  auto post_budgets_reward = process_adv_pending_payouts(_post_budget_service);
103  auto banner_budgets_reward = process_adv_pending_payouts(_banner_budget_service);
104  auto adv_budgets_reward = post_budgets_reward + banner_budgets_reward;
105 
106  close_empty_budgets(_post_budget_service);
107  close_empty_budgets(_banner_budget_service);
108 
109  return adv_budgets_reward;
110 }
111 
112 template <budget_type budget_type_v>
113 asset process_funds::process_adv_pending_payouts(adv_budget_service_i<budget_type_v>& budget_svc)
114 {
115  auto pending_budgets = budget_svc.get_pending_budgets();
116 
117  std::vector<budget_outgo_operation> allocated_cash_ops;
118  std::vector<budget_owner_income_operation> returned_cash_ops;
119  for (const adv_budget_object<budget_type_v>& budget : pending_budgets)
120  {
121  if (budget.budget_pending_outgo.amount > 0)
122  allocated_cash_ops.emplace_back(budget_type_v, budget.owner, budget.id._id, budget.budget_pending_outgo);
123 
124  if (budget.owner_pending_income.amount > 0)
125  returned_cash_ops.emplace_back(budget_type_v, budget.owner, budget.id._id, budget.owner_pending_income);
126  }
127 
128  auto adv_budgets_reward = budget_svc.perform_pending_payouts(pending_budgets);
129 
130  for (const auto& op : allocated_cash_ops)
131  _virt_op_emitter.push_virtual_operation(op);
132 
133  for (const auto& op : returned_cash_ops)
134  _virt_op_emitter.push_virtual_operation(op);
135 
136  return adv_budgets_reward;
137 }
138 
139 template <budget_type budget_type_v>
140 void process_funds::close_empty_budgets(adv_budget_service_i<budget_type_v>& budget_svc)
141 {
142  auto empty_budgets = budget_svc.get_empty_budgets();
143 
144  for (const auto& b : empty_budgets)
145  {
146  _virt_op_emitter.push_virtual_operation(budget_closing_operation(budget_type_v, b.get().owner, b.get().id._id));
147 
148  budget_svc.remove(b);
149  }
150 }
151 
152 void process_funds::distribute_reward(const asset& users_reward)
153 {
154  // clang-format off
156  asset witness_reward = users_reward * utils::make_fraction(SCORUM_WITNESS_PER_BLOCK_REWARD_PERCENT, SCORUM_100_PERCENT);
157  asset active_sp_holder_reward = users_reward * utils::make_fraction(SCORUM_ACTIVE_SP_HOLDERS_PER_BLOCK_REWARD_PERCENT ,SCORUM_100_PERCENT);
158  asset content_reward = users_reward - witness_reward - active_sp_holder_reward;
159  // clang-format on
160 
161  process_witness_reward_in_sp_migration().adjust_witness_reward(_ctx, witness_reward);
162 
163  FC_ASSERT(content_reward.amount >= 0, "content_reward(${r}) must not be less zero", ("r", content_reward));
164 
165  distribute_witness_reward(witness_reward);
166 
167  distribute_active_sp_holders_reward(active_sp_holder_reward);
168 
169  pay_content_reward(content_reward);
170 }
171 
172 void process_funds::distribute_witness_reward(const asset& witness_reward)
173 {
174  const auto& cwit = _witness_svc.get(_dprops_service.get().current_witness);
175 
176  if (cwit.schedule != witness_object::top20 && cwit.schedule != witness_object::timeshare)
177  {
178  wlog("Encountered unknown witness type for witness: ${w}", ("w", cwit.owner));
179  }
180 
181  const auto& witness = _account_service.get_account(cwit.owner);
182 
183  pay_witness_reward(witness, witness_reward);
184 
185  if (witness_reward.amount != 0)
186  _virt_op_emitter.push_virtual_operation(producer_reward_operation(witness.name, witness_reward));
187 }
188 
189 void process_funds::distribute_active_sp_holders_reward(const asset& reward)
190 {
191  asset total_reward = get_activity_reward(reward);
192 
193  asset distributed_reward = asset(0, reward.symbol());
194 
195  auto active_sp_holders_array = _account_service.get_active_sp_holders();
196  if (!active_sp_holders_array.empty())
197  {
198  // distribute
199  asset total_sp = std::accumulate(active_sp_holders_array.begin(), active_sp_holders_array.end(),
200  asset(0, SP_SYMBOL), [&](asset& accumulator, const account_object& account) {
201  return accumulator += account.vote_reward_competitive_sp;
202  });
203 
204  if (total_sp.amount > 0)
205  {
207  for (const account_object& account : active_sp_holders_array)
208  {
209  // It is used SP balance amount of account to calculate reward either in SP or SCR tokens
210  asset account_reward
211  = total_reward * utils::make_fraction(account.vote_reward_competitive_sp.amount, total_sp.amount);
212 
213  if (_hardfork_svc.has_hardfork(SCORUM_HARDFORK_0_2))
214  {
215  pay_account_pending_reward(account, account_reward);
216  }
217  else
218  {
219  pay_account_reward(account, account_reward);
220  }
221 
222  distributed_reward += account_reward;
223 
224  if (!_hardfork_svc.has_hardfork(SCORUM_HARDFORK_0_2) && account_reward.amount > 0)
225  {
226  rewarded[account.name] = account_reward;
227  }
228  }
229 
230  if (!_hardfork_svc.has_hardfork(SCORUM_HARDFORK_0_2) && !rewarded.empty())
231  {
232  _virt_op_emitter.push_virtual_operation(
233  active_sp_holders_reward_legacy_operation{ std::move(rewarded) });
234  }
235  }
236  }
237 
238  // put undistributed money in special fund
239  pay_activity_reward(total_reward - distributed_reward);
240 }
241 
242 void process_funds::pay_account_reward(const account_object& account, const asset& reward)
243 {
244  if (reward.amount <= 0)
245  return;
246 
247  if (reward.symbol() == SCORUM_SYMBOL)
248  {
249  _account_service.increase_balance(account, reward);
250  }
251  else
252  {
253  _account_service.create_scorumpower(account, reward);
254  }
255 }
256 
257 void process_funds::pay_account_pending_reward(const account_object& account, const asset& reward)
258 {
259  if (reward.amount <= 0)
260  return;
261 
262  if (reward.symbol() == SCORUM_SYMBOL)
263  {
264  _account_service.increase_pending_balance(account, reward);
265  }
266  else
267  {
268  _account_service.increase_pending_scorumpower(account, reward);
269  }
270 }
271 
272 void process_funds::pay_witness_reward(const account_object& witness, const asset& reward)
273 {
274  if (reward.symbol() == SCORUM_SYMBOL)
275  {
276  _dprops_service.update([&](dynamic_global_property_object& p) { p.total_witness_reward_scr += reward; });
277  }
278  else
279  {
280  _dprops_service.update([&](dynamic_global_property_object& p) { p.total_witness_reward_sp += reward; });
281  }
282 
283  pay_account_reward(witness, reward);
284 }
285 
286 void process_funds::pay_content_reward(const asset& reward)
287 {
288  if (reward.amount <= 0)
289  return;
290 
291  if (reward.symbol() == SCORUM_SYMBOL)
292  {
293  _content_reward_fund_scr_svc.update(
294  [&](content_reward_fund_scr_object& rfo) { rfo.activity_reward_balance += reward; });
295  }
296  else
297  {
298  _content_reward_fund_sp_svc.update(
299  [&](content_reward_fund_sp_object& rfo) { rfo.activity_reward_balance += reward; });
300  }
301 }
302 
303 void process_funds::pay_activity_reward(const asset& reward)
304 {
305  if (reward.amount <= 0)
306  return;
307 
308  if (reward.symbol() == SCORUM_SYMBOL)
309  {
310  reward_balance_algorithm<voters_reward_scr_service_i> balancer(_voter_reward_scr_svc);
311  balancer.increase_ballance(reward);
312  }
313  else
314  {
315  reward_balance_algorithm<voters_reward_sp_service_i> balancer(_voter_reward_sp_svc);
316  balancer.increase_ballance(reward);
317  }
318 }
319 
320 const asset process_funds::get_activity_reward(const asset& reward)
321 {
322  if (reward.symbol() == SCORUM_SYMBOL)
323  {
324  reward_balance_algorithm<voters_reward_scr_service_i> balancer(_voter_reward_scr_svc);
325  return reward + balancer.take_block_reward();
326  }
327  else
328  {
329  reward_balance_algorithm<voters_reward_sp_service_i> balancer(_voter_reward_sp_svc);
330  return reward + balancer.take_block_reward();
331  }
332 }
333 
334 bool process_funds::apply_mainnet_schedule_crutches()
335 {
336  // We have bug on mainnet: signed_block was applied, but undo_session wasn't pushed, therefore DB state roll backed
337  // and witnesses were not rewarded. It leads us to mismatching of schedule for working and newly synced nodes. Next
338  // code fixes it.
339  if (_block_num == 1650380 || // fix reward for headshot witness
340  _block_num == 1808664) // fix reward for addit-yury witness
341  {
342  return true;
343  }
344 
345  return false;
346 }
347 }
348 }
349 }
virtual void on_apply(block_task_context &)
account_name_type current_witness
Head block signed by current witness.
#define SP_SYMBOL
Definition: config.hpp:104
#define SCORUM_SYMBOL
Definition: config.hpp:102
#define SCORUM_WITNESS_PER_BLOCK_REWARD_PERCENT
Definition: config.hpp:206
#define SCORUM_100_PERCENT
Definition: config.hpp:200
#define SCORUM_ACTIVE_SP_HOLDERS_PER_BLOCK_REWARD_PERCENT
Definition: config.hpp:207
#define SCORUM_DEV_TEAM_PER_BLOCK_REWARD_PERCENT
Definition: config.hpp:205
#define debug_log(CTX, FORMAT,...)
Definition: debug_log.hpp:3
reward_fund_object< content_reward_fund_sp_object_type, SP_SYMBOL > content_reward_fund_sp_object
reward_fund_object< content_reward_fund_scr_object_type, SCORUM_SYMBOL > content_reward_fund_scr_object
Definition: asset.cpp:15
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 void increase_pending_balance(const account_object &account, const asset &amount)=0
virtual void increase_pending_scorumpower(const account_object &account, const asset &amount)=0
virtual account_refs_type get_active_sp_holders() const =0
virtual budgets_type get_pending_budgets() const =0
virtual asset perform_pending_payouts(const budgets_type &budgets)=0
virtual bool is_exists() const =0
virtual void update(const modifier_type &modifier)=0
virtual const object_type & get() const =0
virtual void push_virtual_operation(const operation &op)=0
virtual asset allocate_cash(const fund_budget_object &budget)=0
virtual bool has_hardfork(uint32_t hardfork) const =0
virtual const witness_object & get(const account_name_type &owner) const =0