Scorum
blockchain_history_plugin.cpp
Go to the documentation of this file.
6 
7 #include <scorum/account_identity/impacted.hpp>
8 
11 
15 
16 #include <fc/smart_ref_impl.hpp>
17 #include <fc/thread/thread.hpp>
18 
19 #include <boost/algorithm/string.hpp>
20 
21 #define SCORUM_NAMESPACE_PREFIX "scorum::protocol::"
22 
23 namespace scorum {
24 namespace blockchain_history {
25 
26 namespace detail {
27 
28 using namespace scorum::protocol;
29 
31 {
32 public:
34  : _self(_plugin)
35  {
36  }
38  {
39  }
40 
42  {
43  return _self.database();
44  }
45 
46  void initialize()
47  {
48  chain::database& db = database();
49 
61 
62  db.pre_apply_operation.connect([&](const operation_notification& note) { on_operation(note); });
63  }
64 
65  const operation_object& create_operation_obj(const operation_notification& note);
66  void update_filtered_operation_index(const operation_object& object, const operation& op);
67  void on_operation(const operation_notification& note);
68 
70  flat_map<account_name_type, account_name_type> _tracked_accounts;
71  bool _filter_content = false;
72  bool _blacklist = false;
73  flat_set<std::string> _op_list;
74 };
75 
77 {
78  database& _db;
79  const operation_object& _obj;
80  account_name_type _item;
81 
82 public:
84  : _db(db)
85  , _obj(obj)
86  , _item(i)
87  {
88  }
89 
90  template <typename Op> void operator()(const Op&) const
91  {
92  push_history<account_history_object>(_obj);
93  }
94 
95  void operator()(const transfer_operation&) const
96  {
97  push_history<account_history_object>(_obj);
98  push_history<account_transfers_to_scr_history_object>(_obj);
99  }
100 
102  {
103  push_history<account_history_object>(_obj);
104  push_history<account_transfers_to_sp_history_object>(_obj);
105  }
106 
108  {
109  push_history<account_history_object>(_obj);
110 
111  if (_item == op.account)
112  push_progress<account_withdrawals_to_scr_history_object>(_obj);
113 
114  push_history<account_withdrawals_to_scr_history_object>(_obj);
115  }
116 
118  {
119  push_history<account_history_object>(_obj);
120 
121  if (_item == op.from_account)
122  push_progress<account_withdrawals_to_scr_history_object>(_obj);
123  }
124 
126  {
127  push_history<account_history_object>(_obj);
128 
129  if (_item == op.from_account)
130  push_progress<account_withdrawals_to_scr_history_object>(_obj);
131  }
132 
134  {
135  push_history<account_history_object>(_obj);
136 
137  if (_item == op.from_account)
138  push_progress<account_withdrawals_to_scr_history_object>(_obj);
139  }
140 
141 private:
142  template <typename history_object_type> void push_history(const operation_object& op) const
143  {
144  const auto& hist_idx = _db.get_index<account_history_index<history_object_type>, by_account>();
145  auto hist_itr = hist_idx.lower_bound(_item);
146  uint32_t sequence = 0;
147  if (hist_itr != hist_idx.end() && hist_itr->account == _item)
148  sequence = hist_itr->sequence + 1;
149 
150  _db.create<history_object_type>([&](history_object_type& ahist) {
151  ahist.account = _item;
152  ahist.sequence = sequence;
153  ahist.op = op.id;
154  });
155  }
156 
157  template <typename history_object_type> void push_progress(const operation_object& op) const
158  {
159  const auto& idx = _db.get_index<account_history_index<history_object_type>, by_account>();
160  auto it = idx.lower_bound(_item);
161  if (it != idx.end() && it->account == _item)
162  {
163  _db.modify<history_object_type>(*it, [&](history_object_type& h) { h.progress.push_back(op.id); });
164  }
165  }
166 };
167 
169 {
170  database& _db;
171  const operation_object& _obj;
172 
173 public:
174  using result_type = void;
175 
177  : _db(db)
178  , _obj(obj)
179  {
180  }
181 
182  template <typename Op> void operator()(const Op&) const
183  {
184  // do nothing.
185  }
186 
188  {
189  op.proposal_op.weak_visit(
191  push_devcommittee_history<devcommittee_history_object>(_obj);
192  push_devcommittee_progress<devcommittee_withdrawals_to_scr_history_object>(_obj);
193  push_devcommittee_history<devcommittee_withdrawals_to_scr_history_object>(_obj);
194  },
196  push_devcommittee_history<devcommittee_history_object>(_obj);
197  push_devcommittee_history<devcommittee_transfers_to_scr_history_object>(_obj);
198  },
200  push_devcommittee_history<devcommittee_history_object>(_obj);
201  },
203  push_devcommittee_history<devcommittee_history_object>(_obj);
204  },
206  push_devcommittee_history<devcommittee_history_object>(_obj);
207  });
208  }
209 
211  {
212  push_devcommittee_history<devcommittee_history_object>(_obj);
213  push_devcommittee_progress<devcommittee_withdrawals_to_scr_history_object>(_obj);
214  }
215 
217  {
218  push_devcommittee_history<devcommittee_history_object>(_obj);
219  push_devcommittee_progress<devcommittee_withdrawals_to_scr_history_object>(_obj);
220  }
221 
222 private:
223  template <typename history_object_type> void push_devcommittee_history(const operation_object& op) const
224  {
225  _db.create<history_object_type>([&](history_object_type& ahist) { ahist.op = op.id; });
226  }
227 
228  template <typename history_object_type> void push_devcommittee_progress(const operation_object& op) const
229  {
230  const auto& idx = _db.get_index<devcommittee_history_index<history_object_type>, by_id_desc>();
231  if (!idx.empty())
232  {
233  _db.modify<history_object_type>(*idx.begin(), [&](history_object_type& h) { h.progress.push_back(op.id); });
234  }
235  }
236 };
237 
239 {
240  operation_visitor_filter(const flat_set<std::string>& filter, bool blacklist)
241  : _filter(filter)
242  , _blacklist(blacklist)
243  {
244  }
245 
246  template <typename T> bool operator()(const T& op) const
247  {
248  if (_filter.find(fc::get_typename<T>::name()) != _filter.end())
249  {
250  if (!_blacklist)
251  return true;
252  }
253  else
254  {
255  if (_blacklist)
256  return true;
257  }
258 
259  return false;
260  }
261 
262 private:
263  const flat_set<std::string>& _filter;
264  bool _blacklist;
265 };
266 
268 {
270 
271  return db.create<operation_object>([&](operation_object& obj) {
272  obj.trx_id = note.trx_id;
273  obj.block = note.block;
274  obj.trx_in_block = note.trx_in_block;
275  obj.op_in_trx = note.op_in_trx;
276  obj.timestamp = db.head_block_time();
277  auto size = fc::raw::pack_size(note.op);
278  obj.serialized_op.resize(size);
279  fc::datastream<char*> ds(obj.serialized_op.data(), size);
280  fc::raw::pack(ds, note.op);
281  });
282 }
283 
285  const operation& op)
286 {
288 
289  if (is_virtual_operation(op))
290  {
292  [&](filtered_virt_operations_history_object& obj) { obj.op = object.id; });
293  }
294  else
295  {
297  [&](filtered_not_virt_operations_history_object& obj) { obj.op = object.id; });
298  }
299  if (is_market_operation(op))
300  {
302  [&](filtered_market_operations_history_object& obj) { obj.op = object.id; });
303  }
304 }
305 
307 {
308  flat_set<account_name_type> impacted;
310 
311  if (_filter_content && !note.op.visit(operation_visitor_filter(_op_list, _blacklist)))
312  return;
313 
314  account_identity::operation_get_impacted_accounts(note.op, impacted);
315 
316  const operation_object& new_obj = create_operation_obj(note);
317  update_filtered_operation_index(new_obj, note.op);
318 
319  note.op.visit(devcommittee_operation_visitor(db, new_obj));
320 
321  for (const auto& item : impacted)
322  {
323  auto itr = _tracked_accounts.lower_bound(item);
324 
325  /*
326  * The map containing the ranges uses the key as the lower bound and the value as the upper bound.
327  * Because of this, if a value exists with the range (key, value], then calling lower_bound on
328  * the map will return the key of the next pair. Under normal circumstances of those ranges not
329  * intersecting, the value we are looking for will not be present in range that is returned via
330  * lower_bound.
331  *
332  * Consider the following example using ranges ["a","c"], ["g","i"]
333  * If we are looking for "bob", it should be tracked because it is in the lower bound.
334  * However, lower_bound( "bob" ) returns an iterator to ["g","i"]. So we need to decrement the iterator
335  * to get the correct range.
336  *
337  * If we are looking for "g", lower_bound( "g" ) will return ["g","i"], so we need to make sure we don't
338  * decrement.
339  *
340  * If the iterator points to the end, we should check the previous (equivalent to rbegin)
341  *
342  * And finally if the iterator is at the beginning, we should not decrement it for obvious reasons
343  */
344  if (itr != _tracked_accounts.begin()
345  && ((itr != _tracked_accounts.end() && itr->first != item) || itr == _tracked_accounts.end()))
346  {
347  --itr;
348  }
349 
350  if (!_tracked_accounts.size() || (itr != _tracked_accounts.end() && itr->first <= item && item <= itr->second))
351  {
352  note.op.visit(operation_visitor(db, new_obj, item));
353  }
354  }
355 }
356 
357 } // end namespace detail
358 
360  : plugin(app)
361  , _my(new detail::blockchain_history_plugin_impl(*this))
362 {
363  // ilog("Loading account history plugin" );
364 }
365 
367 {
368 }
369 
371 {
373 }
374 
375 void blockchain_history_plugin::plugin_set_program_options(boost::program_options::options_description& cli,
376  boost::program_options::options_description& cfg)
377 {
378  cli.add_options()(
379  "track-account-range", boost::program_options::value<std::vector<std::string>>()->composing()->multitoken(),
380  "Defines a range of accounts to track as a json pair [\"from\",\"to\"] [from,to] Can be specified multiple "
381  "times")("history-whitelist-ops", boost::program_options::value<std::vector<std::string>>()->composing(),
382  "Defines a list of operations which will be explicitly logged.")(
383  "history-blacklist-ops", boost::program_options::value<std::vector<std::string>>()->composing(),
384  "Defines a list of operations which will be explicitly ignored.");
387  cfg.add(cli);
388 }
389 
390 void blockchain_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)
391 {
392  try
393  {
394  typedef std::pair<account_name_type, account_name_type> pairstring;
395  LOAD_VALUE_SET(options, "track-account-range", _my->_tracked_accounts, pairstring);
396 
399 
400  if (options.count("history-whitelist-ops"))
401  {
402  _my->_filter_content = true;
403  _my->_blacklist = false;
404 
405  for (auto& arg : options.at("history-whitelist-ops").as<std::vector<std::string>>())
406  {
407  std::vector<std::string> ops;
408  boost::split(ops, arg, boost::is_any_of(" \t,"));
409 
410  for (const std::string& op : ops)
411  {
412  if (op.size())
413  _my->_op_list.insert(SCORUM_NAMESPACE_PREFIX + op);
414  }
415  }
416 
417  ilog("Account History: whitelisting ops ${o}", ("o", _my->_op_list));
418  }
419  else if (options.count("history-blacklist-ops"))
420  {
421  _my->_filter_content = true;
422  _my->_blacklist = true;
423  for (auto& arg : options.at("history-blacklist-ops").as<std::vector<std::string>>())
424  {
425  std::vector<std::string> ops;
426  boost::split(ops, arg, boost::is_any_of(" \t,"));
427 
428  for (const std::string& op : ops)
429  {
430  if (op.size())
431  _my->_op_list.insert(SCORUM_NAMESPACE_PREFIX + op);
432  }
433  }
434 
435  ilog("Account History: blacklisting ops ${o}", ("o", _my->_op_list));
436  }
437 
438  _my->initialize();
439  }
440  FC_LOG_AND_RETHROW()
441 
442  print_greeting();
443 }
444 
446 {
450 }
451 
452 flat_map<account_name_type, account_name_type> blockchain_history_plugin::tracked_accounts() const
453 {
454  return _my->_tracked_accounts;
455 }
456 }
457 }
458 
#define API_ACCOUNT_HISTORY
#define API_BLOCKCHAIN_HISTORY
#define SCORUM_NAMESPACE_PREFIX
#define BLOCKCHAIN_HISTORY_PLUGIN_NAME
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.
flat_map< account_name_type, account_name_type > tracked_accounts() const
std::unique_ptr< detail::blockchain_history_plugin_impl > _my
flat_map< account_name_type, account_name_type > _tracked_accounts
void update_filtered_operation_index(const operation_object &object, const operation &op)
const operation_object & create_operation_obj(const operation_notification &note)
void operator()(const devpool_to_devpool_vesting_withdraw_operation &op) const
void operator()(const devpool_finished_vesting_withdraw_operation &op) const
void operator()(const acc_to_acc_vesting_withdraw_operation &op) const
void operator()(const transfer_to_scorumpower_operation &) const
void operator()(const acc_to_devpool_vesting_withdraw_operation &op) const
void operator()(const acc_finished_vesting_withdraw_operation &op) const
operation_visitor(database &db, const operation_object &obj, const account_name_type &i)
void operator()(const withdraw_scorumpower_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 operation_notification &)> pre_apply_operation
Definition: database.hpp:209
void set_options(const boost::program_options::variables_map &options)
Definition: config_api.cpp:62
boost::program_options::options_description get_options_descriptions() const
Definition: config_api.cpp:48
#define API_DEVCOMMITTEE_HISTORY
filtered_operation_index< filtered_market_operations_history > filtered_market_operations_history_index
filtered_operation_index< filtered_virt_operations_history > filtered_virt_operations_history_index
devcommittee_history_index< devcommittee_withdrawals_to_scr_history_object > devcommittee_withdrawals_to_scr_history_index
shared_multi_index_container< operation_object, indexed_by< ordered_unique< tag< by_id >, member< operation_object, operation_object::id_type, &operation_object::id > >, ordered_unique< tag< by_location >, composite_key< operation_object, member< operation_object, uint32_t, &operation_object::block >, member< operation_object, uint32_t, &operation_object::trx_in_block >, member< operation_object, uint16_t, &operation_object::op_in_trx >, member< operation_object, operation_object::id_type, &operation_object::id > > >, ordered_unique< tag< by_timestamp >, composite_key< operation_object, member< operation_object, fc::time_point_sec, &operation_object::timestamp >, member< operation_object, operation_object::id_type, &operation_object::id > > >, ordered_unique< tag< by_transaction_id >, composite_key< operation_object, member< operation_object, transaction_id_type, &operation_object::trx_id >, member< operation_object, operation_object::id_type, &operation_object::id > > > > > operation_index
account_history_index< account_withdrawals_to_scr_history_object > account_withdrawals_to_scr_history_index
devcommittee_history_index< devcommittee_history_object > devcommittee_operations_full_history_index
shared_multi_index_container< history_object_t, indexed_by< ordered_unique< tag< by_id >, member< history_object_t, typename history_object_t::id_type, &history_object_t::id > >, ordered_unique< tag< by_account >, composite_key< history_object_t, member< history_object_t, account_name_type, &history_object_t::account >, member< history_object_t, uint32_t, &history_object_t::sequence > >, composite_key_compare< std::less< account_name_type >, std::greater< uint32_t > >> >> account_history_index
account_history_index< account_transfers_to_sp_history_object > account_transfers_to_sp_history_index
devcommittee_history_index< devcommittee_transfers_to_scr_history_object > devcommittee_transfers_to_scr_history_index
account_history_index< account_history_object > account_operations_full_history_index
filtered_operation_index< filtered_not_virt_operations_history > filtered_not_virt_operations_history_index
account_history_index< account_transfers_to_scr_history_object > account_transfers_to_scr_history_index
size_t size(db_index &db_idx)
Definition: db_accessor.hpp:25
fc::static_variant< vote_operation, comment_operation, transfer_operation, transfer_to_scorumpower_operation, withdraw_scorumpower_operation, account_create_by_committee_operation, account_create_operation, account_create_with_delegation_operation, account_update_operation, witness_update_operation, account_witness_vote_operation, account_witness_proxy_operation, delete_comment_operation, comment_options_operation, set_withdraw_scorumpower_route_to_account_operation, set_withdraw_scorumpower_route_to_dev_pool_operation, prove_authority_operation, request_account_recovery_operation, recover_account_operation, change_recovery_account_operation, escrow_approve_operation, escrow_dispute_operation, escrow_release_operation, escrow_transfer_operation, decline_voting_rights_operation, delegate_scorumpower_operation, create_budget_operation, close_budget_operation, proposal_vote_operation, proposal_create_operation, atomicswap_initiate_operation, atomicswap_redeem_operation, atomicswap_refund_operation, close_budget_by_advertising_moderator_operation, update_budget_operation, create_game_operation, cancel_game_operation, update_game_markets_operation, update_game_start_time_operation, post_game_results_operation, post_bet_operation, cancel_pending_bets_operation, delegate_sp_from_reg_pool_operation, create_nft_operation, update_nft_meta_operation, create_game_round_operation, update_game_round_result_operation, adjust_nft_experience_operation, update_nft_name_operation, author_reward_operation, comment_benefficiary_reward_operation, comment_payout_update_operation, comment_reward_operation, curation_reward_operation, hardfork_operation, producer_reward_operation, active_sp_holders_reward_operation, return_scorumpower_delegation_operation, shutdown_witness_operation, witness_miss_block_operation, expired_contract_refund_operation, acc_finished_vesting_withdraw_operation, devpool_finished_vesting_withdraw_operation, acc_to_acc_vesting_withdraw_operation, devpool_to_acc_vesting_withdraw_operation, acc_to_devpool_vesting_withdraw_operation, devpool_to_devpool_vesting_withdraw_operation, proposal_virtual_operation, budget_outgo_operation, budget_owner_income_operation, active_sp_holders_reward_legacy_operation, budget_closing_operation, bets_matched_operation, game_status_changed_operation, bet_resolved_operation, bet_cancelled_operation, bet_restored_operation, bet_updated_operation > operation
Definition: operations.hpp:112
fc::fixed_string_16 account_name_type
Definition: types.hpp:62
bool is_virtual_operation(const operation &op)
Definition: operations.cpp:41
bool is_market_operation(const operation &op)
Definition: operations.cpp:26
Definition: asset.cpp:15
config_api & get_api_config(std::string api_name)
Definition: config_api.cpp:114
#define LOAD_VALUE_SET(options, name, container, type)
Definition: plugin.hpp:145
#define SCORUM_DEFINE_PLUGIN(plugin_name, plugin_class)
Definition: plugin.hpp:156
operation_visitor_filter(const flat_set< std::string > &filter, bool blacklist)
const protocol::transaction_id_type trx_id
Transfers SCR from one account to another.