Scorum
budgets.cpp
Go to the documentation of this file.
2 #include <algorithm>
3 
5 
6 #include <boost/range/adaptor/filtered.hpp>
7 #include <boost/lambda/lambda.hpp>
8 #include <boost/multi_index/detail/unbounded.hpp>
9 
12 
13 #include <scorum/utils/math.hpp>
14 
15 namespace scorum {
16 namespace chain {
17 namespace detail {
18 asset fund_calculate_per_block(time_point_sec start_date, time_point_sec end_date, const asset& balance)
19 {
20  FC_ASSERT(start_date < end_date, "Start time ${1} must be less end time ${2}", ("1", start_date)("2", end_date));
21 
22  auto per_block = balance;
23 
24  uint32_t delta_in_sec = end_date.sec_since_epoch() - start_date.sec_since_epoch();
25 
26  per_block *= SCORUM_BLOCK_INTERVAL;
27  per_block /= delta_in_sec;
28 
29  // non zero budget must return at least one satoshi
30  per_block.amount = std::max(per_block.amount, share_type(1));
31 
32  return per_block;
33 }
34 
35 asset adv_calculate_per_block(time_point_sec start,
36  time_point_sec deadline,
37  time_point_sec last_block_time,
38  const asset& balance)
39 {
40  FC_ASSERT(start <= deadline, "Start time ${1} must be less or equal end time ${2}", ("1", start)("2", deadline));
41 
42  auto aligned_start_sec
43  = utils::ceil(start.sec_since_epoch(), last_block_time.sec_since_epoch(), SCORUM_BLOCK_INTERVAL);
44 
45  auto aligned_deadline_sec
46  = utils::ceil(deadline.sec_since_epoch(), last_block_time.sec_since_epoch(), SCORUM_BLOCK_INTERVAL);
47 
48  auto per_blocks_count = 1 + ((aligned_deadline_sec - aligned_start_sec) / SCORUM_BLOCK_INTERVAL);
49  auto per_block = balance / per_blocks_count;
50 
51  // non zero budget must return at least one satoshi
52  per_block.amount = std::max(per_block.amount, share_type(1));
53 
54  return per_block;
55 }
56 }
57 
60  , _dprops_svc(db.dynamic_global_property_service())
61 {
62 }
63 
64 const fund_budget_object& dbs_fund_budget::create_budget(const asset& balance, time_point_sec start, time_point_sec end)
65 {
66  try
67  {
68  FC_ASSERT(balance.symbol() == SP_SYMBOL, "Invalid asset type (symbol).");
69  FC_ASSERT(start < end, "Invalid dates.");
70 
71  auto per_block = detail::fund_calculate_per_block(start, end, balance);
72 
73  return create([&](fund_budget_object& budget) {
74  budget.created = _dprops_svc.head_block_time();
75  budget.start = start;
76  budget.deadline = end;
77  budget.balance = balance;
78  budget.per_block = per_block;
79  });
80  }
81  FC_CAPTURE_AND_RETHROW((balance)(start)(end))
82 }
83 
85 {
86  auto allocated = std::min(budget.balance, budget.per_block);
87 
88  update(budget, [&](fund_budget_object& b) { b.balance -= allocated; });
89  if (budget.balance.amount <= 0)
90  remove(budget);
91 
92  return allocated;
93 }
94 
95 template <budget_type budget_type_v>
97  : dbs_service_base<typename budget_service_traits<budget_type_v>::service_type>(db)
98  , _dgp_svc(db.dynamic_global_property_service())
99  , _account_svc(db.account_service())
100 {
101 }
102 
103 template <budget_type budget_type_v>
106  const account_name_type& owner,
107  const asset& balance,
108  time_point_sec start,
109  time_point_sec end,
110  const std::string& json_metadata)
111 {
112  try
113  {
114  FC_ASSERT(balance.symbol() == SCORUM_SYMBOL, "Invalid asset type (symbol).");
115  FC_ASSERT(balance.amount > 0, "Invalid balance.");
116 
117  auto head_block_time = _dgp_svc.head_block_time();
118  auto per_block = detail::adv_calculate_per_block(start, end, head_block_time, balance);
119 
120  auto aligned_start_sec
121  = utils::ceil(start.sec_since_epoch(), head_block_time.sec_since_epoch(), SCORUM_BLOCK_INTERVAL);
122  auto cashout_sec = aligned_start_sec + SCORUM_ADVERTISING_CASHOUT_PERIOD_SEC - SCORUM_BLOCK_INTERVAL;
123 
124  const auto& budget = this->create([&](adv_budget_object<budget_type_v>& budget) {
125  budget.uuid = uuid;
126  budget.owner = owner;
127 #ifndef IS_LOW_MEM
128  fc::from_string(budget.json_metadata, json_metadata);
129 #endif
130  budget.created = head_block_time;
131  budget.cashout_time = fc::time_point_sec(cashout_sec);
132  budget.start = start;
133  budget.deadline = end;
134  budget.balance = balance;
135  budget.per_block = per_block;
136  });
137 
138  _account_svc.decrease_balance(_account_svc.get_account(owner), balance);
139 
140  update_totals([&](adv_total_stats::budget_type_stat& statistic) { statistic.volume += balance; });
141 
142  return budget;
143  }
144  FC_CAPTURE_AND_RETHROW((owner)(balance)(start)(end)(json_metadata))
145 }
146 
147 template <budget_type budget_type_v>
150 {
151  try
152  {
153  return this->get_by(id);
154  }
155  FC_CAPTURE_AND_RETHROW((id))
156 }
157 
158 template <budget_type budget_type_v>
160 {
161  try
162  {
163  return this->template get_by<by_uuid>(uuid);
164  }
165  FC_CAPTURE_AND_RETHROW((uuid))
166 }
167 
168 template <budget_type budget_type_v> bool dbs_advertising_budget<budget_type_v>::is_exists(const uuid_type& id) const
169 {
170  try
171  {
172  return nullptr != this->template find_by<by_uuid>(id);
173  }
174  FC_CAPTURE_AND_RETHROW((id))
175 }
176 
177 template <budget_type budget_type_v>
180 {
181  try
182  {
183  return this->find_by(id);
184  }
185  FC_CAPTURE_AND_RETHROW((id))
186 }
187 
188 template <budget_type budget_type_v>
190 {
191  try
192  {
193  return this->template get_range_by<by_id>(::boost::multi_index::unbounded, ::boost::multi_index::unbounded);
194  }
195  FC_CAPTURE_AND_RETHROW(())
196 }
197 
198 template <budget_type budget_type_v>
200 dbs_advertising_budget<budget_type_v>::get_top_budgets(const fc::time_point_sec& until, uint16_t limit) const
201 {
202  namespace ba = boost::adaptors;
203  try
204  {
206  result.reserve(limit);
207 
208  // TODO: will be refactored using db_accessors
209  auto& idx = this->db_impl().template get_index<adv_budget_index<budget_type_v>, by_per_block>();
210  auto from = idx.begin();
211  auto to = idx.lower_bound(false); // including
212 
213  auto rng = boost::make_iterator_range(from, to)
214  | ba::filtered([&](const adv_budget_object<budget_type_v>& obj) { return obj.start <= until; });
215 
216  // TODO: will be refactored using take_n range adaptor
217  for (auto it = rng.begin(); limit && it != rng.end(); ++it, --limit)
218  {
219  result.push_back(std::cref(*it));
220  }
221  return result;
222  }
223  FC_CAPTURE_AND_RETHROW((until)(limit))
224 }
225 
226 template <budget_type budget_type_v>
228 dbs_advertising_budget<budget_type_v>::get_top_budgets(const fc::time_point_sec& until) const
229 {
230  try
231  {
232  return this->get_top_budgets(until, -1);
233  }
234  FC_CAPTURE_AND_RETHROW((until))
235 }
236 
237 template <budget_type budget_type_v>
238 std::set<std::string>
239 dbs_advertising_budget<budget_type_v>::lookup_budget_owners(const std::string& lower_bound_owner_name,
240  uint32_t limit) const
241 {
242  try
243  {
244  std::set<std::string> result;
245 
246  const auto& budgets_by_owner_name
247  = this->db_impl().template get_index<adv_budget_index<budget_type_v>, by_owner_name>();
248 
249  // prepare output if limit > 0
250  for (auto itr = budgets_by_owner_name.lower_bound(lower_bound_owner_name);
251  limit && itr != budgets_by_owner_name.end(); ++itr)
252  {
253  --limit;
254  result.insert(itr->owner);
255  }
256  return result;
257  }
258  FC_CAPTURE_AND_RETHROW((lower_bound_owner_name)(limit))
259 }
260 
261 template <budget_type budget_type_v>
264 {
265  try
266  {
267  return this->template get_range_by<by_owner_name>(owner <= ::boost::lambda::_1, ::boost::lambda::_1 <= owner);
268  }
269  FC_CAPTURE_AND_RETHROW((owner))
270 }
271 
272 template <budget_type budget_type_v>
275 {
276  try
277  {
278  auto head_time = _dgp_svc.head_block_time();
279  return this->template get_range_by<by_cashout_time>(boost::multi_index::unbounded,
280  ::boost::lambda::_1 <= head_time);
281  }
282  FC_CAPTURE_AND_RETHROW()
283 }
284 
285 template <budget_type budget_type_v>
287 {
288  this->update(budget, [&](adv_budget_object<budget_type_v>& b) { b.balance -= budget.per_block; });
289 
290  update_totals([&](adv_total_stats::budget_type_stat& statistic) { statistic.volume -= budget.per_block; });
291 
292  if (budget.deadline <= _dgp_svc.head_block_time() || budget.balance.amount == 0)
293  finish_budget(budget.uuid);
294 
295  return budget.per_block;
296 }
297 
298 template <budget_type budget_type_v>
300  const asset& owner_incoming,
301  const asset& budget_outgoing)
302 {
303  this->update(budget, [&](adv_budget_object<budget_type_v>& b) {
304  b.owner_pending_income += owner_incoming;
305  b.budget_pending_outgo += budget_outgoing;
306  });
307 
308  update_totals([&](adv_total_stats::budget_type_stat& statistic) {
309  statistic.owner_pending_income += owner_incoming;
310  statistic.budget_pending_outgo += budget_outgoing;
311  });
312 }
313 
314 template <budget_type budget_type_v>
316 {
317  auto budgets_outgo = asset(0, SCORUM_SYMBOL);
318  for (const adv_budget_object<budget_type_v>& budget : budgets)
319  {
320  budgets_outgo += budget.budget_pending_outgo;
321  _account_svc.increase_balance(_account_svc.get_account(budget.owner), budget.owner_pending_income);
322 
323  update_totals([&](adv_total_stats::budget_type_stat& statistic) {
324  statistic.owner_pending_income -= budget.owner_pending_income;
325  statistic.budget_pending_outgo -= budget.budget_pending_outgo;
326  });
327 
328  this->update(budget, [&](adv_budget_object<budget_type_v>& b) {
331  b.cashout_time = _dgp_svc.head_block_time() + SCORUM_ADVERTISING_CASHOUT_PERIOD_SEC;
332  });
333  }
334 
335  return budgets_outgo;
336 }
337 
338 template <budget_type budget_type_v> void dbs_advertising_budget<budget_type_v>::finish_budget(const uuid_type& uuid)
339 {
340  const auto& budget = get(uuid);
341 
342  update_totals([&](adv_total_stats::budget_type_stat& statistic) {
343  statistic.owner_pending_income += budget.balance;
344  statistic.volume -= budget.balance;
345  });
346 
347  this->update(budget, [&](adv_budget_object<budget_type_v>& b) {
348  b.cashout_time = _dgp_svc.head_block_time();
350  b.balance.amount = 0;
351  });
352 }
353 
354 template <budget_type budget_type_v>
357 {
358  auto zero = asset(0, SCORUM_SYMBOL);
359  auto empty_budgets = this->template get_range_by<by_balances>(
360  boost::multi_index::unbounded, ::boost::lambda::_1 <= std::make_tuple(zero, zero, zero));
361 
362  return empty_budgets;
363 }
364 
365 template <budget_type budget_type_v>
367  std::function<void(adv_total_stats::budget_type_stat&)> callback)
368 {
369  if (budget_type_v == budget_type::banner)
370  {
371  _dgp_svc.update([&](dynamic_global_property_object& dgp) { callback(dgp.advertising.banner_budgets); });
372  }
373  else if (budget_type_v == budget_type::post)
374  {
375  _dgp_svc.update([&](dynamic_global_property_object& dgp) { callback(dgp.advertising.post_budgets); });
376  }
377  else
378  {
379  FC_THROW("unsuported budget type");
380  }
381 }
382 
383 template class dbs_advertising_budget<budget_type::post>;
384 template class dbs_advertising_budget<budget_type::banner>;
385 }
386 }
tracks the blockchain state in an extensible manner
Definition: database.hpp:52
const adv_budget_object< budget_type_v > * find(const oid< adv_budget_object< budget_type_v >> &id) const override
Definition: budgets.cpp:179
budgets_type get_budgets() const override
Definition: budgets.cpp:189
std::set< std::string > lookup_budget_owners(const std::string &lower_bound_owner_name, uint32_t limit) const override
Definition: budgets.cpp:239
typename adv_budget_service_i< budget_type_v >::budgets_type budgets_type
Definition: budgets.hpp:112
void finish_budget(const uuid_type &uuid) override
Definition: budgets.cpp:338
const adv_budget_object< budget_type_v > & create_budget(const uuid_type &uuid, const account_name_type &owner, const asset &balance, fc::time_point_sec start_date, fc::time_point_sec end_date, const std::string &json_metadata) override
Definition: budgets.cpp:105
budgets_type get_pending_budgets() const override
Definition: budgets.cpp:274
budgets_type get_top_budgets(const fc::time_point_sec &until, uint16_t limit) const override
Definition: budgets.cpp:200
void update_pending_payouts(const adv_budget_object< budget_type_v > &budget, const asset &owner_incoming, const asset &budget_outgoing) override
Definition: budgets.cpp:299
budgets_type get_empty_budgets() const override
Definition: budgets.cpp:356
asset allocate_cash(const adv_budget_object< budget_type_v > &budget) override
Definition: budgets.cpp:286
asset perform_pending_payouts(const budgets_type &budgets) override
Definition: budgets.cpp:315
asset allocate_cash(const fund_budget_object &budget) override
Definition: budgets.cpp:84
dbs_fund_budget(database &db)
Definition: budgets.cpp:58
const fund_budget_object & create_budget(const asset &balance, fc::time_point_sec start, fc::time_point_sec end) override
Definition: budgets.cpp:64
virtual const object_type & create(const modifier_type &modifier) override
virtual void update(const modifier_type &modifier) override
adv_total_stats advertising
this section display information about advertising totals
#define SP_SYMBOL
Definition: config.hpp:104
#define SCORUM_SYMBOL
Definition: config.hpp:102
#define SCORUM_BLOCK_INTERVAL
Definition: config.hpp:171
const TObject * find_by(db_index &db_idx, const Key &arg)
Definition: db_accessor.hpp:88
const TObject & get_by(db_index &db_idx, const Key &arg)
Definition: db_accessor.hpp:79
const TObject & create(db_index &db_idx, modifier_type< TObject > modifier)
Definition: db_accessor.hpp:30
const unbounded_placeholder unbounded
asset adv_calculate_per_block(time_point_sec start, time_point_sec deadline, time_point_sec block_time_sec, const asset &balance)
Definition: budgets.cpp:35
asset fund_calculate_per_block(time_point_sec start, time_point_sec deadline, const asset &balance)
Definition: budgets.cpp:18
void update(fc::flat_map< uuid_type, bet_resolved_operation > &results, const bet_data &bet, asset income, uuid_type game_uuid, bet_resolve_kind kind)
fc::safe< share_value_type > share_type
Definition: types.hpp:73
fc::fixed_string_16 account_name_type
Definition: types.hpp:62
Definition: asset.cpp:15
boost::uuids::uuid uuid_type
Definition: types.hpp:53
asset budget_pending_outgo
pending volume which will go to the activity pool
asset owner_pending_income
pending volume which will be returned to budget owners
budget_type_stat banner_budgets
total's statistic for advertising banner budgets
virtual fc::time_point_sec head_block_time() const =0
asset_symbol_type symbol() const
Definition: asset.hpp:32
share_type amount
Definition: asset.hpp:31