Scorum
blockchain_monitoring_plugin.cpp
Go to the documentation of this file.
5 
9 
14 
17 
18 #include <chrono>
19 
20 namespace scorum {
21 namespace blockchain_monitoring {
22 
23 namespace detail {
24 
25 using namespace scorum::protocol;
26 
29 {
30  typedef std::chrono::high_resolution_clock Clock;
31  Clock::time_point _block_start_time = Clock::time_point::min();
32  Clock::duration _last_block_processing_duration = Clock::duration::zero();
33 
34  void start()
35  {
36  _block_start_time = Clock::now();
37  }
38 
39  void stop()
40  {
41  if (_block_start_time != Clock::time_point::min())
42  {
43  _last_block_processing_duration = Clock::now() - _block_start_time;
44  _block_start_time = Clock::time_point::min();
45  }
46  }
47 
48 public:
50  {
51  db.pre_applied_block.connect([&](const signed_block& b) { this->start(); });
52  db.applied_block.connect([&](const signed_block& b) { this->stop(); });
53  }
54 
55  std::chrono::microseconds get_last_block_duration() const
56  {
57  return std::chrono::duration_cast<std::chrono::microseconds>(_last_block_processing_duration);
58  }
59 };
62  : public common_statistics::common_statistics_plugin_impl<bucket_object, blockchain_monitoring_plugin>
63 {
64 public:
66 
68  : base_plugin_impl(plugin)
69  , _timer(plugin.database())
70  {
71  }
73  {
74  }
75 
76 private:
77  virtual void process_bucket_creation(const bucket_object& bucket) override
78  {
79  auto& db = _self.database();
80 
81  db.modify<bucket_object>(bucket, [&](bucket_object& bo) { bo.blocks = 1; });
82  }
83 
84  virtual void process_block(const bucket_object& bucket, const signed_block& b) override;
85 
86  virtual void process_pre_operation(const bucket_object& bucket, const operation_notification& o) override;
87 
88  virtual void process_post_operation(const bucket_object& bucket, const operation_notification& o) override;
89 
90  template <typename TSourceId>
91  void collect_withdraw_stats(const bucket_object& bucket, const asset& vesting_shares, const TSourceId& source_id);
92 };
93 
95 {
96 private:
97  chain::database& _db;
98  const bucket_object& _bucket;
99 
100 public:
102  : _db(db)
103  , _bucket(b)
104  {
105  }
106 
107  template <typename T> void operator()(const T&) const
108  {
109  }
110 
111  void operator()(const transfer_operation& op) const
112  {
113  _db.modify(_bucket, [&](bucket_object& b) {
114  b.transfers++;
115 
116  if (op.amount.symbol() == SCORUM_SYMBOL)
118  });
119  }
120 
121  void operator()(const account_create_operation& op) const
122  {
123  _db.modify(_bucket, [&](bucket_object& b) { b.paid_accounts_created++; });
124  }
125 
127  {
128  _db.modify(_bucket, [&](bucket_object& b) { b.paid_accounts_created++; });
129  }
130 
132  {
133  _db.modify(_bucket, [&](bucket_object& b) { b.free_accounts_created++; });
134  }
135 
136  void operator()(const comment_operation& op) const
137  {
138  _db.modify(_bucket, [&](bucket_object& b) {
139  auto& comment = _db.obtain_service<dbs_comment>().get(op.author, op.permlink);
140 
141  if (comment.created == _db.head_block_time())
142  {
143  if (comment.parent_author.length())
144  b.replies++;
145  else
146  b.root_comments++;
147  }
148  else
149  {
150  if (comment.parent_author.length())
151  b.reply_edits++;
152  else
153  b.root_comment_edits++;
154  }
155  });
156  }
157 
158  void operator()(const vote_operation& op) const
159  {
160  _db.modify(_bucket, [&](bucket_object& b) {
161  const auto& cv_idx = _db.get_index<comment_vote_index>().indices().get<by_comment_voter>();
162  const auto& comment = _db.obtain_service<dbs_comment>().get(op.author, op.permlink);
163  const auto& voter = _db.account_service().get_account(op.voter);
164  const auto itr = cv_idx.find(boost::make_tuple(comment.id, voter.id));
165 
166  if (itr->num_changes)
167  {
168  if (comment.parent_author.size())
169  b.new_reply_votes++;
170  else
171  b.new_root_votes++;
172  }
173  else
174  {
175  if (comment.parent_author.size())
177  else
178  b.changed_root_votes++;
179  }
180  });
181  }
182 
183  void operator()(const author_reward_operation& op) const
184  {
185  _db.modify(_bucket, [&](bucket_object& b) {
186  b.payouts++;
187  auto reward_symbol = op.reward.symbol();
188  if (SCORUM_SYMBOL == reward_symbol)
189  {
191  }
192  else if (SP_SYMBOL == reward_symbol)
193  {
195  }
196  });
197  }
198 
199  void operator()(const curation_reward_operation& op) const
200  {
201  _db.modify(_bucket, [&](bucket_object& b) {
202  auto reward_symbol = op.reward.symbol();
203  if (SCORUM_SYMBOL == reward_symbol)
204  {
206  }
207  else if (SP_SYMBOL == reward_symbol)
208  {
210  }
211  });
212  }
213 
215  {
216  _db.modify(_bucket, [&](bucket_object& b) {
219  });
220  }
221 
223  {
224  const auto& account = _db.account_service().get_account(op.from_account);
225 
226  collect_withdraw_stats(account.id, account.scorumpower);
227  }
228 
230  {
231  const auto& dev_pool = _db.dev_pool_service().get();
232 
233  collect_withdraw_stats(dev_pool.id, dev_pool.sp_balance);
234  }
235 
237  {
238  collect_withdraw_stats(op.withdrawn);
239  }
240 
242  {
243  collect_withdraw_stats(op.withdrawn);
244  }
245 
247  {
248  collect_withdraw_stats(op.withdrawn);
249  }
250 
252  {
253  collect_withdraw_stats(op.withdrawn);
254  }
255 
257  {
258  op.proposal_op.weak_visit([&](const development_committee_transfer_operation& op) {
259  _db.modify(_bucket, [&](bucket_object& b) {
260  b.transfers++;
261 
262  if (op.amount.symbol() == SCORUM_SYMBOL)
263  b.scorum_transferred += op.amount.amount;
264  });
265  });
266  }
267 
269  {
270  _db.modify(_bucket, [&](bucket_object& b) { b.missed_blocks[op.block_num] = op.owner; });
271  }
272 
273  template <typename TSourceId> void collect_withdraw_stats(const TSourceId& source_id, const asset& source_sp) const
274  {
275  const auto& withdraw_scorumpower_service = _db.obtain_service<chain::dbs_withdraw_scorumpower>();
276 
277  asset vesting_withdraw_rate = asset(0, SP_SYMBOL);
278 
279  if (withdraw_scorumpower_service.is_exists(source_id))
280  {
281  const auto& wvo = withdraw_scorumpower_service.get(source_id);
282  vesting_withdraw_rate = wvo.vesting_withdraw_rate;
283  }
284 
285  _db.modify(_bucket, [&](bucket_object& b) {
287 
288  b.vesting_withdraw_rate_delta -= vesting_withdraw_rate.amount;
289  });
290  }
291 
292  void collect_withdraw_stats(const asset& withdrawn) const
293  {
294  _db.modify(_bucket, [&](bucket_object& b) {
295 
297 
298  if (withdrawn.symbol() == SCORUM_SYMBOL)
299  b.scorumpower_withdrawn += withdrawn.amount;
300  else
301  b.scorumpower_transferred += withdrawn.amount;
302  });
303  }
304 };
305 
306 void blockchain_monitoring_plugin_impl::process_block(const bucket_object& bucket, const signed_block& b)
307 {
308  auto& db = _self.database();
309 
310  uint32_t trx_size = 0;
311  uint32_t num_trx = b.transactions.size();
312 
313  for (const auto& trx : b.transactions)
314  {
315  trx_size += fc::raw::pack_size(trx);
316  }
317 
318  db.modify(bucket, [&](bucket_object& bo) {
319  bo.blocks++;
320  bo.transactions += num_trx;
321  bo.bandwidth += trx_size;
322  });
323 }
324 
325 void blockchain_monitoring_plugin_impl::process_pre_operation(const bucket_object& bucket,
326  const operation_notification& note)
327 {
328  auto& db = _self.database();
329 
330  note.op.weak_visit(
331  [&](const delete_comment_operation& op) {
332  auto comment = db.obtain_service<dbs_comment>().get(op.author, op.permlink);
333 
334  db.modify(bucket, [&](bucket_object& b) {
335  if (comment.parent_author.length())
336  b.replies_deleted++;
337  else
338  b.root_comments_deleted++;
339  });
340  },
341  [&](const withdraw_scorumpower_operation& op) {
342  collect_withdraw_stats(bucket, op.scorumpower, db.account_service().get_account(op.account).id);
343  },
344  [&](const proposal_virtual_operation& op) {
345  op.proposal_op.weak_visit([&](const development_committee_withdraw_vesting_operation& proposal_op) {
346  collect_withdraw_stats(bucket, proposal_op.vesting_shares, db.dev_pool_service().get().id);
347  });
348  });
349 }
350 
351 void blockchain_monitoring_plugin_impl::process_post_operation(const bucket_object& bucket,
352  const operation_notification& o)
353 {
354  auto& db = _self.database();
355 
356  if (!is_virtual_operation(o.op))
357  {
358  db.modify(bucket, [&](bucket_object& b) { b.operations++; });
359  }
360  o.op.visit(operation_process(db, bucket));
361 }
362 
363 template <typename TSourceId>
364 void blockchain_monitoring_plugin_impl::collect_withdraw_stats(const bucket_object& bucket,
365  const asset& vesting_shares,
366  const TSourceId& source_id)
367 {
368  auto& db = _self.database();
369 
370  auto new_vesting_withdrawal_rate = vesting_shares.amount / SCORUM_VESTING_WITHDRAW_INTERVALS;
371  if (vesting_shares.amount > 0 && new_vesting_withdrawal_rate == 0)
372  new_vesting_withdrawal_rate = 1;
373 
374  const auto& withdraw_scorumpower_service = db.obtain_service<chain::dbs_withdraw_scorumpower>();
375 
376  asset vesting_withdraw_rate = asset(0, SP_SYMBOL);
377 
378  if (withdraw_scorumpower_service.is_exists(source_id))
379  {
380  const auto& wvo = withdraw_scorumpower_service.get(source_id);
381  vesting_withdraw_rate = wvo.vesting_withdraw_rate;
382  }
383 
384  db.modify(bucket, [&](bucket_object& b) {
385  if (vesting_withdraw_rate.amount > 0)
386  b.modified_vesting_withdrawal_requests++;
387  else
388  b.new_vesting_withdrawal_requests++;
389 
390  b.vesting_withdraw_rate_delta += new_vesting_withdrawal_rate - vesting_withdraw_rate.amount;
391  });
392 }
393 
394 } // detail
395 
397  : plugin(app)
398  , _my(new detail::blockchain_monitoring_plugin_impl(*this))
399 {
400 }
401 
403 {
404 }
405 
406 void blockchain_monitoring_plugin::plugin_set_program_options(boost::program_options::options_description& cli,
407  boost::program_options::options_description& cfg)
408 {
409  cli.add_options()(
410  "chain-stats-bucket-size",
411  boost::program_options::value<std::string>()->default_value("[60,3600,21600,86400,604800,2592000]"),
412  "Track blockchain statistics by grouping orders into buckets of equal size measured in seconds specified as a "
413  "JSON array of numbers")(
414  "chain-stats-history-per-bucket", boost::program_options::value<uint32_t>()->default_value(100),
415  "How far back in time to track history for each bucket size, measured in the number of buckets (default: 100)");
416  cfg.add(cli);
417 }
418 
419 void blockchain_monitoring_plugin::plugin_initialize(const boost::program_options::variables_map& options)
420 {
421  try
422  {
423  if (options.count("chain-stats-bucket-size"))
424  {
425  const std::string& buckets = options["chain-stats-bucket-size"].as<std::string>();
426  _my->_tracked_buckets = fc::json::from_string(buckets).as<flat_set<uint32_t>>();
427  }
428  if (options.count("chain-stats-history-per-bucket"))
429  _my->_maximum_history_per_bucket_size = options["chain-stats-history-per-bucket"].as<uint32_t>();
430 
431  ilog("chain-stats-bucket-size: ${b}", ("b", _my->_tracked_buckets));
432  ilog("chain-stats-history-per-bucket: ${h}", ("h", _my->_maximum_history_per_bucket_size));
433 
434  _my->initialize();
435  }
436  FC_CAPTURE_AND_RETHROW()
437 
438  print_greeting();
439 }
440 
442 {
445 }
446 
447 const flat_set<uint32_t>& blockchain_monitoring_plugin::get_tracked_buckets() const
448 {
449  return _my->_tracked_buckets;
450 }
451 
453 {
454  return _my->_maximum_history_per_bucket_size;
455 }
456 
458 {
459  return std::chrono::duration_cast<std::chrono::microseconds>(_my->_timer.get_last_block_duration()).count();
460 }
461 }
462 } // scorum::blockchain_monitoring
463 
SCORUM_DEFINE_PLUGIN(blockchain_monitoring, scorum::blockchain_monitoring::blockchain_monitoring_plugin)
#define API_BLOCKCHAIN_STATISTICS
void register_api_factory(const std::string &name, std::function< fc::api_ptr(const api_context &)> factory)
application & app() const
Definition: plugin.hpp:119
void print_greeting()
Definition: plugin.cpp:69
virtual void plugin_initialize(const boost::program_options::variables_map &options) override
Perform early startup routines and register plugin indexes, callbacks, etc.
virtual void plugin_startup() override
Begin normal runtime operations.
virtual void plugin_set_program_options(boost::program_options::options_description &cli, boost::program_options::options_description &cfg) override
Fill in command line parameters used by the plugin.
void operator()(const transfer_to_scorumpower_operation &op) const
void operator()(const acc_to_devpool_vesting_withdraw_operation &op) const
void operator()(const proposal_virtual_operation &op) const
void operator()(const devpool_finished_vesting_withdraw_operation &) const
void operator()(const witness_miss_block_operation &op) const
void operator()(const curation_reward_operation &op) const
void operator()(const account_create_by_committee_operation &op) const
void operator()(const devpool_to_devpool_vesting_withdraw_operation &op) const
void operator()(const account_create_with_delegation_operation &op) const
void operator()(const devpool_to_acc_vesting_withdraw_operation &op) const
operation_process(chain::database &db, const bucket_object &b)
void collect_withdraw_stats(const TSourceId &source_id, const asset &source_sp) const
void operator()(const acc_finished_vesting_withdraw_operation &op) const
void operator()(const acc_to_acc_vesting_withdraw_operation &op) const
tracks the blockchain state in an extensible manner
Definition: database.hpp:52
database(uint32_t opt)
Definition: database.cpp:168
time_point_sec head_block_time() const
Definition: database.cpp:1222
fc::signal< void(const signed_block &)> applied_block
Definition: database.hpp:221
fc::signal< void(const signed_block &)> pre_applied_block
Definition: database.hpp:211
#define SCORUM_VESTING_WITHDRAW_INTERVALS
Definition: config.hpp:155
#define SP_SYMBOL
Definition: config.hpp:104
#define SCORUM_SYMBOL
Definition: config.hpp:102
bool is_virtual_operation(const operation &op)
Definition: operations.cpp:41
Definition: asset.cpp:15
#define API_NODE_MONITORING
uint32_t transfers
Account/devpool to account transfers.
Definition: metrics.hpp:28
uint32_t changed_reply_votes
Changed votes on replies.
Definition: metrics.hpp:51
share_type scorumpower_paid_to_authors
Amount of SP paid to authors.
Definition: metrics.hpp:54
uint32_t reply_edits
Edits to replies.
Definition: metrics.hpp:46
uint32_t root_comment_edits
Edits to root comments.
Definition: metrics.hpp:43
uint32_t vesting_withdrawals_processed
Number of vesting withdrawals.
Definition: metrics.hpp:32
uint32_t transfers_to_scorumpower
Transfers of SCR into SP.
Definition: metrics.hpp:29
uint32_t replies
Replies to comments.
Definition: metrics.hpp:45
uint32_t new_root_votes
New votes on root comments.
Definition: metrics.hpp:48
uint32_t free_accounts_created
Accounts created with fee.
Definition: metrics.hpp:26
uint32_t new_reply_votes
New votes on replies.
Definition: metrics.hpp:50
uint32_t blocks
Blocks produced.
Definition: metrics.hpp:19
uint32_t finished_vesting_withdrawals
Processed vesting withdrawals that are now finished.
Definition: metrics.hpp:33
share_type scr_paid_to_curators
Amount of SCR paid to curators.
Definition: metrics.hpp:55
uint32_t paid_accounts_created
Accounts created with fee.
Definition: metrics.hpp:25
share_type scorumpower_withdrawn
Amount of SP withdrawn to SCR.
Definition: metrics.hpp:37
uint32_t root_comments
Top level root comments.
Definition: metrics.hpp:42
share_type scorumpower_transferred
Amount of SP transferred to another account.
Definition: metrics.hpp:38
share_type scorum_transferred
SCR transferred from account to account.
Definition: metrics.hpp:35
share_type scorum_transferred_to_scorumpower
Amount of SCR vested.
Definition: metrics.hpp:36
share_type scorumpower_paid_to_curators
Amount of SP paid to curators.
Definition: metrics.hpp:56
share_type scr_paid_to_authors
Amount of SCR paid to authors.
Definition: metrics.hpp:53
uint32_t changed_root_votes
Changed votes on root comments.
Definition: metrics.hpp:49
uint32_t payouts
Number of comment payouts.
Definition: metrics.hpp:52
fc::shared_map< uint32_t, account_name_type > missed_blocks
map missed block to witness which missed
Creates new account by registration committee.
asset_symbol_type symbol() const
Definition: asset.hpp:32
share_type amount
Definition: asset.hpp:31
asset reward
all reward for comment author (with from_children_payout)
std::vector< signed_transaction > transactions
Definition: block.hpp:13
Transfers SCR from one account to another.
asset amount
The amount of asset to transfer from from to to.