BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
database_api.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "database_api_impl.hxx"
26 
27 #include <graphene/app/util.hpp>
29 #include <graphene/chain/hardfork.hpp>
32 
33 #include <fc/crypto/hex.hpp>
35 
36 #include <boost/range/iterator_range.hpp>
37 
38 #include <cctype>
39 
41 
42 namespace graphene { namespace app {
43 
45 // //
46 // Constructors //
47 // //
49 
50 database_api::database_api( graphene::chain::database& db, const application_options* app_options )
51  : my( new database_api_impl( db, app_options ) ) {}
52 
54 
56 :_db(db), _app_options(app_options)
57 {
58  dlog("creating database api ${x}", ("x",int64_t(this)) );
59  _new_connection = _db.new_objects.connect([this](const vector<object_id_type>& ids,
60  const flat_set<account_id_type>& impacted_accounts) {
61  on_objects_new(ids, impacted_accounts);
62  });
63  _change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids,
64  const flat_set<account_id_type>& impacted_accounts) {
65  on_objects_changed(ids, impacted_accounts);
66  });
67  _removed_connection = _db.removed_objects.connect([this](const vector<object_id_type>& ids,
68  const vector<const object*>& objs,
69  const flat_set<account_id_type>& impacted_accounts) {
70  on_objects_removed(ids, objs, impacted_accounts);
71  });
73 
74  _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){
77  });
78  try
79  {
81  .get_secondary_index<graphene::api_helper_indexes::amount_in_collateral_index>();
82  }
83  catch( fc::assert_exception& e )
84  {
86  }
87 }
88 
90 {
91  dlog("freeing database api ${x}", ("x",int64_t(this)) );
92 }
93 
95 // //
96 // Objects //
97 // //
99 
100 fc::variants database_api::get_objects( const vector<object_id_type>& ids, optional<bool> subscribe )const
101 {
102  return my->get_objects( ids, subscribe );
103 }
104 
105 fc::variants database_api_impl::get_objects( const vector<object_id_type>& ids, optional<bool> subscribe )const
106 {
107  bool to_subscribe = get_whether_to_subscribe( subscribe );
108 
109  fc::variants result;
110  result.reserve(ids.size());
111 
112  std::transform(ids.begin(), ids.end(), std::back_inserter(result),
113  [this,to_subscribe](object_id_type id) -> fc::variant {
114  if(auto obj = _db.find_object(id))
115  {
116  if( to_subscribe && !id.is<operation_history_id_type>() && !id.is<account_transaction_history_id_type>() )
117  this->subscribe_to_item( id );
118  return obj->to_variant();
119  }
120  return {};
121  });
122 
123  return result;
124 }
125 
127 // //
128 // Subscriptions //
129 // //
131 
132 void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
133 {
134  my->set_subscribe_callback( cb, notify_remove_create );
135 }
136 
137 void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
138 {
139  if( notify_remove_create )
140  {
142  "Subscribing to universal object creation and removal is disallowed in this server." );
143  }
144 
145  cancel_all_subscriptions(false, false);
146 
147  _subscribe_callback = cb;
148  _notify_remove_create = notify_remove_create;
149 }
150 
152 {
153  my->set_auto_subscription( enable );
154 }
155 
157 {
159 }
160 
161 void database_api::set_pending_transaction_callback( std::function<void(const variant&)> cb )
162 {
163  my->set_pending_transaction_callback( cb );
164 }
165 
166 void database_api_impl::set_pending_transaction_callback( std::function<void(const variant&)> cb )
167 {
169 }
170 
171 void database_api::set_block_applied_callback( std::function<void(const variant& block_id)> cb )
172 {
173  my->set_block_applied_callback( cb );
174 }
175 
176 void database_api_impl::set_block_applied_callback( std::function<void(const variant& block_id)> cb )
177 {
179 }
180 
182 {
183  my->cancel_all_subscriptions(true, true);
184 }
185 
186 void database_api_impl::cancel_all_subscriptions( bool reset_callback, bool reset_market_subscriptions )
187 {
188  if ( reset_callback )
189  _subscribe_callback = std::function<void(const fc::variant&)>();
190 
191  if ( reset_market_subscriptions )
192  _market_subscriptions.clear();
193 
194  _notify_remove_create = false;
195  _subscribed_accounts.clear();
196  static fc::bloom_parameters param(10000, 1.0/100, 1024*8*8*2);
198 }
199 
201 // //
202 // Blocks and transactions //
203 // //
205 
206 optional<block_header> database_api::get_block_header(uint32_t block_num)const
207 {
208  return my->get_block_header( block_num );
209 }
210 
211 optional<block_header> database_api_impl::get_block_header(uint32_t block_num) const
212 {
213  auto result = _db.fetch_block_by_number(block_num);
214  if(result)
215  return *result;
216  return {};
217 }
218 map<uint32_t, optional<block_header>> database_api::get_block_header_batch(const vector<uint32_t> block_nums)const
219 {
220  return my->get_block_header_batch( block_nums );
221 }
222 
223 map<uint32_t, optional<block_header>> database_api_impl::get_block_header_batch(
224  const vector<uint32_t> block_nums) const
225 {
226  map<uint32_t, optional<block_header>> results;
227  for (const uint32_t block_num : block_nums)
228  {
229  results[block_num] = get_block_header(block_num);
230  }
231  return results;
232 }
233 
234 optional<signed_block> database_api::get_block(uint32_t block_num)const
235 {
236  return my->get_block( block_num );
237 }
238 
239 optional<signed_block> database_api_impl::get_block(uint32_t block_num)const
240 {
241  return _db.fetch_block_by_number(block_num);
242 }
243 
244 processed_transaction database_api::get_transaction( uint32_t block_num, uint32_t trx_in_block )const
245 {
246  return my->get_transaction( block_num, trx_in_block );
247 }
248 
249 optional<signed_transaction> database_api::get_recent_transaction_by_id( const transaction_id_type& id )const
250 {
251  try {
252  return my->_db.get_recent_transaction( id );
253  } catch ( ... ) {
254  return optional<signed_transaction>();
255  }
256 }
257 
258 processed_transaction database_api_impl::get_transaction(uint32_t block_num, uint32_t trx_num)const
259 {
260  auto opt_block = _db.fetch_block_by_number(block_num);
261  FC_ASSERT( opt_block );
262  FC_ASSERT( opt_block->transactions.size() > trx_num );
263  return opt_block->transactions[trx_num];
264 }
265 
267 // //
268 // Globals //
269 // //
271 
273 {
274  return my->get_chain_properties();
275 }
276 
278 {
279  return _db.get(chain_property_id_type());
280 }
281 
283 {
284  return my->get_global_properties();
285 }
286 
288 {
289  return _db.get(global_property_id_type());
290 }
291 
293 {
294  return my->get_config();
295 }
296 
298 {
300 }
301 
303 {
304  return my->get_chain_id();
305 }
306 
308 {
309  return _db.get_chain_id();
310 }
311 
313 {
314  return my->get_dynamic_global_properties();
315 }
316 
318 {
319  return _db.get(dynamic_global_property_id_type());
320 }
321 
323 // //
324 // Keys //
325 // //
327 
328 vector<flat_set<account_id_type>> database_api::get_key_references( vector<public_key_type> key )const
329 {
330  return my->get_key_references( key );
331 }
332 
336 vector<flat_set<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const
337 {
338  // api_helper_indexes plugin is required for accessing the secondary index
340  "api_helper_indexes plugin is not enabled on this server." );
341 
342  const auto configured_limit = _app_options->api_limit_get_key_references;
343  FC_ASSERT( keys.size() <= configured_limit,
344  "Number of querying keys can not be greater than ${configured_limit}",
345  ("configured_limit", configured_limit) );
346 
347  const auto& idx = _db.get_index_type<account_index>();
348  const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
349  const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
350 
351  vector< flat_set<account_id_type> > final_result;
352  final_result.reserve(keys.size());
353 
354  for( auto& key : keys )
355  {
356  address a1( pts_address(key, false, 56) );
357  address a2( pts_address(key, true, 56) );
358  address a3( pts_address(key, false, 0) );
359  address a4( pts_address(key, true, 0) );
360  address a5( key );
361 
362  flat_set<account_id_type> result;
363 
364  for( auto& a : {a1,a2,a3,a4,a5} )
365  {
366  auto itr = refs.account_to_address_memberships.find(a);
367  if( itr != refs.account_to_address_memberships.end() )
368  {
369  result.reserve( result.size() + itr->second.size() );
370  for( auto item : itr->second )
371  {
372  result.insert(item);
373  }
374  }
375  }
376 
377  auto itr = refs.account_to_key_memberships.find(key);
378  if( itr != refs.account_to_key_memberships.end() )
379  {
380  result.reserve( result.size() + itr->second.size() );
381  for( auto item : itr->second ) result.insert(item);
382  }
383  final_result.emplace_back( std::move(result) );
384  }
385 
386  return final_result;
387 }
388 
390 {
391  return my->is_public_key_registered(public_key);
392 }
393 
395 {
396  // Short-circuit
397  if (public_key.empty()) {
398  return false;
399  }
400 
401  // Search among all keys using an existing map of *current* account keys
402  public_key_type key;
403  try {
404  key = public_key_type(public_key);
405  } catch ( ... ) {
406  // An invalid public key was detected
407  return false;
408  }
409 
410  // api_helper_indexes plugin is required for accessing the secondary index
412  "api_helper_indexes plugin is not enabled on this server." );
413 
414  const auto& idx = _db.get_index_type<account_index>();
415  const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
416  const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
417  auto itr = refs.account_to_key_memberships.find(key);
418  bool is_known = itr != refs.account_to_key_memberships.end();
419 
420  return is_known;
421 }
422 
424 // //
425 // Accounts //
426 // //
428 
429 account_id_type database_api::get_account_id_from_string(const std::string& name_or_id)const
430 {
431  return my->get_account_from_string( name_or_id )->id;
432 }
433 
434 vector<optional<account_object>> database_api::get_accounts( const vector<std::string>& account_names_or_ids,
435  optional<bool> subscribe )const
436 {
437  return my->get_accounts( account_names_or_ids, subscribe );
438 }
439 
440 vector<optional<account_object>> database_api_impl::get_accounts( const vector<std::string>& account_names_or_ids,
441  optional<bool> subscribe )const
442 {
443  bool to_subscribe = get_whether_to_subscribe( subscribe );
444  vector<optional<account_object>> result; result.reserve(account_names_or_ids.size());
445  std::transform(account_names_or_ids.begin(), account_names_or_ids.end(), std::back_inserter(result),
446  [this,to_subscribe](std::string id_or_name) -> optional<account_object> {
447 
448  const account_object *account = get_account_from_string(id_or_name, false);
449  if(account == nullptr)
450  return {};
451  if( to_subscribe )
452  subscribe_to_item( account->id );
453  return *account;
454  });
455  return result;
456 }
457 
458 std::map<string,full_account> database_api::get_full_accounts( const vector<string>& names_or_ids,
459  optional<bool> subscribe )
460 {
461  return my->get_full_accounts( names_or_ids, subscribe );
462 }
463 
464 vector<account_statistics_object> database_api::get_top_voters(uint32_t limit)const
465 {
466  return my->get_top_voters( limit );
467 }
468 
469 std::map<std::string, full_account> database_api_impl::get_full_accounts( const vector<std::string>& names_or_ids,
470  optional<bool> subscribe )
471 {
472  FC_ASSERT( _app_options, "Internal error" );
473  const auto configured_limit = _app_options->api_limit_get_full_accounts;
474  FC_ASSERT( names_or_ids.size() <= configured_limit,
475  "Number of querying accounts can not be greater than ${configured_limit}",
476  ("configured_limit", configured_limit) );
477 
478  bool to_subscribe = get_whether_to_subscribe( subscribe );
479 
480  std::map<std::string, full_account> results;
481 
482  for (const std::string& account_name_or_id : names_or_ids)
483  {
484  const account_object* account = get_account_from_string(account_name_or_id, false);
485  if (account == nullptr)
486  continue;
487 
488  if( to_subscribe )
489  {
490  if(_subscribed_accounts.size() < 100) {
491  _subscribed_accounts.insert( account->get_id() );
492  subscribe_to_item( account->id );
493  }
494  }
495 
496  full_account acnt;
497  acnt.account = *account;
498  acnt.statistics = account->statistics(_db);
499  acnt.registrar_name = account->registrar(_db).name;
500  acnt.referrer_name = account->referrer(_db).name;
501  acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name;
502  acnt.votes = lookup_vote_ids( vector<vote_id_type>( account->options.votes.begin(),
503  account->options.votes.end() ) );
504 
505  if (account->cashback_vb)
506  {
507  acnt.cashback_balance = account->cashback_balance(_db);
508  }
509 
510  size_t api_limit_get_full_accounts_lists = static_cast<size_t>(
512 
513  // Add the account's proposals (if the data is available)
515  {
516  const auto& proposal_idx = _db.get_index_type< primary_index< proposal_index > >();
517  const auto& proposals_by_account = proposal_idx.get_secondary_index<
519 
520  auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id );
521  if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )
522  {
523  acnt.proposals.reserve( std::min(required_approvals_itr->second.size(),
524  api_limit_get_full_accounts_lists) );
525  for( auto proposal_id : required_approvals_itr->second )
526  {
527  if(acnt.proposals.size() >= api_limit_get_full_accounts_lists) {
528  acnt.more_data_available.proposals = true;
529  break;
530  }
531  acnt.proposals.push_back(proposal_id(_db));
532  }
533  }
534  }
535 
536  // Add the account's balances
537  const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().
538  get_secondary_index< balances_by_account_index >().get_account_balances( account->id );
539  for( const auto& balance : balances )
540  {
541  if(acnt.balances.size() >= api_limit_get_full_accounts_lists) {
542  acnt.more_data_available.balances = true;
543  break;
544  }
545  acnt.balances.emplace_back(*balance.second);
546  }
547 
548  // Add the account's vesting balances
549  auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>()
550  .equal_range(account->id);
551  for(auto itr = vesting_range.first; itr != vesting_range.second; ++itr)
552  {
553  if(acnt.vesting_balances.size() >= api_limit_get_full_accounts_lists) {
555  break;
556  }
557  acnt.vesting_balances.emplace_back(*itr);
558  }
559 
560  // Add the account's orders
561  auto order_range = _db.get_index_type<limit_order_index>().indices().get<by_account>()
562  .equal_range(account->id);
563  for(auto itr = order_range.first; itr != order_range.second; ++itr)
564  {
565  if(acnt.limit_orders.size() >= api_limit_get_full_accounts_lists) {
566  acnt.more_data_available.limit_orders = true;
567  break;
568  }
569  acnt.limit_orders.emplace_back(*itr);
570  }
571  auto call_range = _db.get_index_type<call_order_index>().indices().get<by_account>().equal_range(account->id);
572  for(auto itr = call_range.first; itr != call_range.second; ++itr)
573  {
574  if(acnt.call_orders.size() >= api_limit_get_full_accounts_lists) {
575  acnt.more_data_available.call_orders = true;
576  break;
577  }
578  acnt.call_orders.emplace_back(*itr);
579  }
580  auto settle_range = _db.get_index_type<force_settlement_index>().indices().get<by_account>()
581  .equal_range(account->id);
582  for(auto itr = settle_range.first; itr != settle_range.second; ++itr)
583  {
584  if(acnt.settle_orders.size() >= api_limit_get_full_accounts_lists) {
586  break;
587  }
588  acnt.settle_orders.emplace_back(*itr);
589  }
590 
591  // get assets issued by user
592  auto asset_range = _db.get_index_type<asset_index>().indices().get<by_issuer>().equal_range(account->id);
593  for(auto itr = asset_range.first; itr != asset_range.second; ++itr)
594  {
595  if(acnt.assets.size() >= api_limit_get_full_accounts_lists) {
596  acnt.more_data_available.assets = true;
597  break;
598  }
599  acnt.assets.emplace_back(itr->id);
600  }
601 
602  // get withdraws permissions
603  auto withdraw_indices = _db.get_index_type<withdraw_permission_index>().indices();
604  auto withdraw_from_range = withdraw_indices.get<by_from>().equal_range(account->id);
605  for(auto itr = withdraw_from_range.first; itr != withdraw_from_range.second; ++itr)
606  {
607  if(acnt.withdraws_from.size() >= api_limit_get_full_accounts_lists) {
609  break;
610  }
611  acnt.withdraws_from.emplace_back(*itr);
612  }
613  auto withdraw_authorized_range = withdraw_indices.get<by_authorized>().equal_range(account->id);
614  for(auto itr = withdraw_authorized_range.first; itr != withdraw_authorized_range.second; ++itr)
615  {
616  if(acnt.withdraws_to.size() >= api_limit_get_full_accounts_lists) {
617  acnt.more_data_available.withdraws_to = true;
618  break;
619  }
620  acnt.withdraws_to.emplace_back(*itr);
621  }
622 
623  // get htlcs
624  auto htlc_from_range = _db.get_index_type<htlc_index>().indices().get<by_from_id>().equal_range(account->id);
625  for(auto itr = htlc_from_range.first; itr != htlc_from_range.second; ++itr)
626  {
627  if(acnt.htlcs_from.size() >= api_limit_get_full_accounts_lists) {
628  acnt.more_data_available.htlcs_from = true;
629  break;
630  }
631  acnt.htlcs_from.emplace_back(*itr);
632  }
633  auto htlc_to_range = _db.get_index_type<htlc_index>().indices().get<by_to_id>().equal_range(account->id);
634  for(auto itr = htlc_to_range.first; itr != htlc_to_range.second; ++itr)
635  {
636  if(acnt.htlcs_to.size() >= api_limit_get_full_accounts_lists) {
637  acnt.more_data_available.htlcs_to = true;
638  break;
639  }
640  acnt.htlcs_to.emplace_back(*itr);
641  }
642 
643  results[account_name_or_id] = acnt;
644  }
645  return results;
646 }
647 
648 vector<account_statistics_object> database_api_impl::get_top_voters(uint32_t limit)const
649 {
650  FC_ASSERT( _app_options, "Internal error" );
651  const auto configured_limit = _app_options->api_limit_get_top_voters;
652  FC_ASSERT( limit <= configured_limit,
653  "limit can not be greater than ${configured_limit}",
654  ("configured_limit", configured_limit) );
655 
656  vector<account_statistics_object> result;
657 
658  auto last_vote_tally_time = _db.get_dynamic_global_properties().last_vote_tally_time;
659  const auto& idx = _db.get_index_type<account_stats_index>().indices().get<by_voting_power_active>();
660 
661  for(auto itr = idx.begin(); result.size() < limit && itr != idx.end() && itr->vote_tally_time >= last_vote_tally_time; ++itr)
662  {
663  result.emplace_back(*itr);
664  }
665 
666  return result;
667 }
668 
669 optional<account_object> database_api::get_account_by_name( string name )const
670 {
671  return my->get_account_by_name( name );
672 }
673 
674 optional<account_object> database_api_impl::get_account_by_name( string name )const
675 {
676  const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
677  auto itr = idx.find(name);
678  if (itr != idx.end())
679  return *itr;
680  return optional<account_object>();
681 }
682 
683 vector<account_id_type> database_api::get_account_references( const std::string account_id_or_name )const
684 {
685  return my->get_account_references( account_id_or_name );
686 }
687 
688 vector<account_id_type> database_api_impl::get_account_references( const std::string account_id_or_name )const
689 {
690  // api_helper_indexes plugin is required for accessing the secondary index
692  "api_helper_indexes plugin is not enabled on this server." );
693 
694  const auto& idx = _db.get_index_type<account_index>();
695  const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
696  const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
697  const account_id_type account_id = get_account_from_string(account_id_or_name)->id;
698  auto itr = refs.account_to_account_memberships.find(account_id);
699  vector<account_id_type> result;
700 
701  if( itr != refs.account_to_account_memberships.end() )
702  {
703  result.reserve( itr->second.size() );
704  for( auto item : itr->second ) result.push_back(item);
705  }
706  return result;
707 }
708 
709 vector<optional<account_object>> database_api::lookup_account_names(const vector<string>& account_names)const
710 {
711  return my->lookup_account_names( account_names );
712 }
713 
714 vector<optional<account_object>> database_api_impl::lookup_account_names(const vector<string>& account_names)const
715 {
716  const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
717  vector<optional<account_object> > result;
718  result.reserve(account_names.size());
719  std::transform(account_names.begin(), account_names.end(), std::back_inserter(result),
720  [&accounts_by_name](const string& name) -> optional<account_object> {
721  auto itr = accounts_by_name.find(name);
722  return itr == accounts_by_name.end()? optional<account_object>() : *itr;
723  });
724  return result;
725 }
726 
727 map<string,account_id_type> database_api::lookup_accounts( const string& lower_bound_name,
728  uint32_t limit,
729  optional<bool> subscribe )const
730 {
731  return my->lookup_accounts( lower_bound_name, limit, subscribe );
732 }
733 
734 map<string,account_id_type> database_api_impl::lookup_accounts( const string& lower_bound_name,
735  uint32_t limit,
736  optional<bool> subscribe )const
737 {
738  FC_ASSERT( _app_options, "Internal error" );
739  const auto configured_limit = _app_options->api_limit_lookup_accounts;
740  FC_ASSERT( limit <= configured_limit,
741  "limit can not be greater than ${configured_limit}",
742  ("configured_limit", configured_limit) );
743 
744  const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
745  map<string,account_id_type> result;
746 
747  if( limit == 0 ) // shortcut to save a database query
748  return result;
749  // In addition to the common auto-subscription rules, here we auto-subscribe if only look for one account
750  bool to_subscribe = (limit == 1 && get_whether_to_subscribe( subscribe ));
751  for( auto itr = accounts_by_name.lower_bound(lower_bound_name);
752  limit-- && itr != accounts_by_name.end();
753  ++itr )
754  {
755  result.insert(make_pair(itr->name, itr->get_id()));
756  if( to_subscribe )
757  subscribe_to_item( itr->id );
758  }
759 
760  return result;
761 }
762 
764 {
765  return my->get_account_count();
766 }
767 
769 {
770  return _db.get_index_type<account_index>().indices().size();
771 }
772 
774 // //
775 // Balances //
776 // //
778 
779 vector<asset> database_api::get_account_balances( const std::string& account_name_or_id,
780  const flat_set<asset_id_type>& assets )const
781 {
782  return my->get_account_balances( account_name_or_id, assets );
783 }
784 
785 vector<asset> database_api_impl::get_account_balances( const std::string& account_name_or_id,
786  const flat_set<asset_id_type>& assets )const
787 {
788  const account_object* account = get_account_from_string(account_name_or_id);
789  account_id_type acnt = account->id;
790  vector<asset> result;
791  if (assets.empty())
792  {
793  // if the caller passes in an empty list of assets, return balances for all assets the account owns
795  const auto& balances = balance_index.get_secondary_index< balances_by_account_index >()
796  .get_account_balances( acnt );
797  for( const auto& balance : balances )
798  result.push_back( balance.second->get_balance() );
799  }
800  else
801  {
802  result.reserve(assets.size());
803 
804  std::transform(assets.begin(), assets.end(), std::back_inserter(result),
805  [this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
806  }
807 
808  return result;
809 }
810 
811 vector<asset> database_api::get_named_account_balances( const std::string& name,
812  const flat_set<asset_id_type>& assets )const
813 {
814  return my->get_account_balances( name, assets );
815 }
816 
817 vector<balance_object> database_api::get_balance_objects( const vector<address>& addrs )const
818 {
819  return my->get_balance_objects( addrs );
820 }
821 
822 vector<balance_object> database_api_impl::get_balance_objects( const vector<address>& addrs )const
823 {
824  try
825  {
826  const auto& bal_idx = _db.get_index_type<balance_index>();
827  const auto& by_owner_idx = bal_idx.indices().get<by_owner>();
828 
829  vector<balance_object> result;
830 
831  for( const auto& owner : addrs )
832  {
833  auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) );
834  while( itr != by_owner_idx.end() && itr->owner == owner )
835  {
836  result.push_back( *itr );
837  ++itr;
838  }
839  }
840  return result;
841  }
842  FC_CAPTURE_AND_RETHROW( (addrs) )
843 }
844 
845 vector<asset> database_api::get_vested_balances( const vector<balance_id_type>& objs )const
846 {
847  return my->get_vested_balances( objs );
848 }
849 
850 vector<asset> database_api_impl::get_vested_balances( const vector<balance_id_type>& objs )const
851 {
852  try
853  {
854  vector<asset> result;
855  result.reserve( objs.size() );
856  auto now = _db.head_block_time();
857  for( auto obj : objs )
858  result.push_back( obj(_db).available( now ) );
859  return result;
860  } FC_CAPTURE_AND_RETHROW( (objs) )
861 }
862 
863 vector<vesting_balance_object> database_api::get_vesting_balances( const std::string account_id_or_name )const
864 {
865  return my->get_vesting_balances( account_id_or_name );
866 }
867 
868 vector<vesting_balance_object> database_api_impl::get_vesting_balances( const std::string account_id_or_name )const
869 {
870  try
871  {
872  const account_id_type account_id = get_account_from_string(account_id_or_name)->id;
873  vector<vesting_balance_object> result;
874  auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>()
875  .equal_range(account_id);
876  std::for_each(vesting_range.first, vesting_range.second,
877  [&result](const vesting_balance_object& balance) {
878  result.emplace_back(balance);
879  });
880  return result;
881  }
882  FC_CAPTURE_AND_RETHROW( (account_id_or_name) );
883 }
884 
886 // //
887 // Assets //
888 // //
890 
891 asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_or_id)const
892 {
893  return my->get_asset_from_string( symbol_or_id )->id;
894 }
895 
896 vector<optional<extended_asset_object>> database_api::get_assets(
897  const vector<std::string>& asset_symbols_or_ids,
898  optional<bool> subscribe )const
899 {
900  return my->get_assets( asset_symbols_or_ids, subscribe );
901 }
902 
903 vector<optional<extended_asset_object>> database_api_impl::get_assets(
904  const vector<std::string>& asset_symbols_or_ids,
905  optional<bool> subscribe )const
906 {
907  bool to_subscribe = get_whether_to_subscribe( subscribe );
908  vector<optional<extended_asset_object>> result; result.reserve(asset_symbols_or_ids.size());
909  std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),
910  [this,to_subscribe](std::string id_or_name) -> optional<extended_asset_object> {
911 
912  const asset_object* asset_obj = get_asset_from_string( id_or_name, false );
913  if( asset_obj == nullptr )
914  return {};
915  if( to_subscribe )
916  subscribe_to_item( asset_obj->id );
917  return extend_asset( *asset_obj );
918  });
919  return result;
920 }
921 
922 vector<extended_asset_object> database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const
923 {
924  return my->list_assets( lower_bound_symbol, limit );
925 }
926 
927 vector<extended_asset_object> database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const
928 {
929  FC_ASSERT( _app_options, "Internal error" );
930  const auto configured_limit = _app_options->api_limit_get_assets;
931  FC_ASSERT( limit <= configured_limit,
932  "limit can not be greater than ${configured_limit}",
933  ("configured_limit", configured_limit) );
934 
935  const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
936  vector<extended_asset_object> result;
937  result.reserve(limit);
938 
939  auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);
940 
941  if( lower_bound_symbol == "" )
942  itr = assets_by_symbol.begin();
943 
944  while(limit-- && itr != assets_by_symbol.end())
945  result.emplace_back( extend_asset( *itr++ ) );
946 
947  return result;
948 }
949 
951 {
952  return my->get_asset_count();
953 }
954 
956 {
957  return _db.get_index_type<asset_index>().indices().size();
958 }
959 
960 vector<extended_asset_object> database_api::get_assets_by_issuer(const std::string& issuer_name_or_id,
961  asset_id_type start, uint32_t limit)const
962 {
963  return my->get_assets_by_issuer(issuer_name_or_id, start, limit);
964 }
965 
966 vector<extended_asset_object> database_api_impl::get_assets_by_issuer(const std::string& issuer_name_or_id,
967  asset_id_type start, uint32_t limit)const
968 {
969  FC_ASSERT( _app_options, "Internal error" );
970  const auto configured_limit = _app_options->api_limit_get_assets;
971  FC_ASSERT( limit <= configured_limit,
972  "limit can not be greater than ${configured_limit}",
973  ("configured_limit", configured_limit) );
974 
975  vector<extended_asset_object> result;
976  const account_id_type account = get_account_from_string(issuer_name_or_id)->id;
977  const auto& asset_idx = _db.get_index_type<asset_index>().indices().get<by_issuer>();
978  auto asset_index_end = asset_idx.end();
979  auto asset_itr = asset_idx.lower_bound(boost::make_tuple(account, start));
980  while(asset_itr != asset_index_end && asset_itr->issuer == account && result.size() < limit)
981  {
982  result.emplace_back( extend_asset( *asset_itr ) );
983  ++asset_itr;
984  }
985  return result;
986 }
987 
988 vector<optional<extended_asset_object>> database_api::lookup_asset_symbols(
989  const vector<string>& symbols_or_ids )const
990 {
991  return my->lookup_asset_symbols( symbols_or_ids );
992 }
993 
994 vector<optional<extended_asset_object>> database_api_impl::lookup_asset_symbols(
995  const vector<string>& symbols_or_ids )const
996 {
997  const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
998  vector<optional<extended_asset_object> > result;
999  result.reserve(symbols_or_ids.size());
1000  std::transform(symbols_or_ids.begin(), symbols_or_ids.end(), std::back_inserter(result),
1001  [this, &assets_by_symbol](const string& symbol_or_id) -> optional<extended_asset_object> {
1002  if( !symbol_or_id.empty() && std::isdigit(symbol_or_id[0]) )
1003  {
1004  auto ptr = _db.find(variant(symbol_or_id, 1).as<asset_id_type>(1));
1005  return ptr == nullptr? optional<extended_asset_object>() : extend_asset( *ptr );
1006  }
1007  auto itr = assets_by_symbol.find(symbol_or_id);
1008  return itr == assets_by_symbol.end()? optional<extended_asset_object>() : extend_asset( *itr );
1009  });
1010  return result;
1011 }
1012 
1014 // //
1015 // Markets / feeds //
1016 // //
1018 
1019 vector<limit_order_object> database_api::get_limit_orders(std::string a, std::string b, uint32_t limit)const
1020 {
1021  return my->get_limit_orders( a, b, limit );
1022 }
1023 
1024 vector<limit_order_object> database_api_impl::get_limit_orders( const std::string& a, const std::string& b,
1025  uint32_t limit )const
1026 {
1027  FC_ASSERT( _app_options, "Internal error" );
1028  const auto configured_limit = _app_options->api_limit_get_limit_orders;
1029  FC_ASSERT( limit <= configured_limit,
1030  "limit can not be greater than ${configured_limit}",
1031  ("configured_limit", configured_limit) );
1032 
1033  const asset_id_type asset_a_id = get_asset_from_string(a)->id;
1034  const asset_id_type asset_b_id = get_asset_from_string(b)->id;
1035 
1036  return get_limit_orders(asset_a_id, asset_b_id, limit);
1037 }
1038 
1039 vector<limit_order_object> database_api::get_limit_orders_by_account( const string& account_name_or_id,
1040  optional<uint32_t> limit, optional<limit_order_id_type> start_id )
1041 {
1042  return my->get_limit_orders_by_account( account_name_or_id, limit, start_id );
1043 }
1044 
1045 vector<limit_order_object> database_api_impl::get_limit_orders_by_account( const string& account_name_or_id,
1046  optional<uint32_t> olimit, optional<limit_order_id_type> ostart_id )
1047 {
1048  uint32_t limit = olimit.valid() ? *olimit : 101;
1049 
1050  FC_ASSERT( _app_options, "Internal error" );
1051  const auto configured_limit = _app_options->api_limit_get_limit_orders_by_account;
1052  FC_ASSERT( limit <= configured_limit,
1053  "limit can not be greater than ${configured_limit}",
1054  ("configured_limit", configured_limit) );
1055 
1056  vector<limit_order_object> results;
1057 
1058  const account_object* account = get_account_from_string(account_name_or_id);
1059  if (account == nullptr)
1060  return results;
1061 
1062  limit_order_id_type start_id = ostart_id.valid() ? *ostart_id : limit_order_id_type();
1063 
1064  const auto& index_by_account = _db.get_index_type<limit_order_index>().indices().get<by_account>();
1065  auto lower_itr = index_by_account.lower_bound( std::make_tuple( account->id, start_id ) );
1066  auto upper_itr = index_by_account.upper_bound( account->id );
1067 
1068  results.reserve( limit );
1069  uint32_t count = 0;
1070  for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
1071  {
1072  const limit_order_object &order = *lower_itr;
1073  results.emplace_back(order);
1074  }
1075 
1076  return results;
1077 }
1078 
1079 vector<limit_order_object> database_api::get_account_limit_orders(
1080  const string& account_name_or_id, const string &base, const string &quote,
1081  uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price )
1082 {
1083  return my->get_account_limit_orders( account_name_or_id, base, quote, limit, ostart_id, ostart_price );
1084 }
1085 
1087  const string& account_name_or_id, const string &base, const string &quote,
1088  uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price )
1089 {
1090  FC_ASSERT( _app_options, "Internal error" );
1091  const auto configured_limit = _app_options->api_limit_get_account_limit_orders;
1092  FC_ASSERT( limit <= configured_limit,
1093  "limit can not be greater than ${configured_limit}",
1094  ("configured_limit", configured_limit) );
1095 
1096  vector<limit_order_object> results;
1097  uint32_t count = 0;
1098 
1099  const account_object* account = get_account_from_string(account_name_or_id);
1100  if (account == nullptr)
1101  return results;
1102 
1103  auto assets = lookup_asset_symbols( {base, quote} );
1104  FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
1105  FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
1106 
1107  auto base_id = assets[0]->id;
1108  auto quote_id = assets[1]->id;
1109 
1110  if (ostart_price.valid()) {
1111  FC_ASSERT(ostart_price->base.asset_id == base_id, "Base asset inconsistent with start price");
1112  FC_ASSERT(ostart_price->quote.asset_id == quote_id, "Quote asset inconsistent with start price");
1113  }
1114 
1115  const auto& index_by_account = _db.get_index_type<limit_order_index>().indices().get<by_account_price>();
1116  limit_order_multi_index_type::index<by_account_price>::type::const_iterator lower_itr;
1117  limit_order_multi_index_type::index<by_account_price>::type::const_iterator upper_itr;
1118 
1119  // if both order_id and price are invalid, query the first page
1120  if ( !ostart_id.valid() && !ostart_price.valid() )
1121  {
1122  lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, price::max(base_id, quote_id)));
1123  }
1124  else if ( ostart_id.valid() )
1125  {
1126  // in case of the order been deleted during page querying
1127  const limit_order_object *p_loo = _db.find(*ostart_id);
1128 
1129  if ( !p_loo )
1130  {
1131  if ( ostart_price.valid() )
1132  {
1133  lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price, *ostart_id));
1134  }
1135  else
1136  {
1137  // start order id been deleted, yet not provided price either
1138  FC_THROW("Order id invalid (maybe just been canceled?), and start price not provided");
1139  }
1140  }
1141  else
1142  {
1143  const limit_order_object &loo = *p_loo;
1144 
1145  // in case of the order not belongs to specified account or market
1146  FC_ASSERT(loo.sell_price.base.asset_id == base_id, "Order base asset inconsistent");
1147  FC_ASSERT(loo.sell_price.quote.asset_id == quote_id, "Order quote asset inconsistent with order");
1148  FC_ASSERT(loo.seller == account->get_id(), "Order not owned by specified account");
1149 
1150  lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, loo.sell_price, *ostart_id));
1151  }
1152  }
1153  else
1154  {
1155  // if reach here start_price must be valid
1156  lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price));
1157  }
1158 
1159  upper_itr = index_by_account.upper_bound(std::make_tuple(account->id, price::min(base_id, quote_id)));
1160 
1161  // Add the account's orders
1162  for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
1163  {
1164  const limit_order_object &order = *lower_itr;
1165  results.emplace_back(order);
1166  }
1167 
1168  return results;
1169 }
1170 
1171 vector<call_order_object> database_api::get_call_orders(const std::string& a, uint32_t limit)const
1172 {
1173  return my->get_call_orders( a, limit );
1174 }
1175 
1176 vector<call_order_object> database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const
1177 {
1178  FC_ASSERT( _app_options, "Internal error" );
1179  const auto configured_limit = _app_options->api_limit_get_call_orders;
1180  FC_ASSERT( limit <= configured_limit,
1181  "limit can not be greater than ${configured_limit}",
1182  ("configured_limit", configured_limit) );
1183 
1184  const asset_object* mia = get_asset_from_string(a);
1185  const auto& call_index = _db.get_index_type<call_order_index>().indices().get<by_collateral>();
1186  price index_price = price::min( mia->bitasset_data(_db).options.short_backing_asset, mia->get_id() );
1187 
1188  vector< call_order_object> result;
1189  auto itr_min = call_index.lower_bound(index_price);
1190  auto itr_max = call_index.upper_bound(index_price.max());
1191  while( itr_min != itr_max && result.size() < limit )
1192  {
1193  result.emplace_back(*itr_min);
1194  ++itr_min;
1195  }
1196  return result;
1197 }
1198 
1199 vector<call_order_object> database_api::get_call_orders_by_account(const std::string& account_name_or_id,
1200  asset_id_type start, uint32_t limit)const
1201 {
1202  return my->get_call_orders_by_account( account_name_or_id, start, limit );
1203 }
1204 
1205 vector<call_order_object> database_api_impl::get_call_orders_by_account(const std::string& account_name_or_id,
1206  asset_id_type start, uint32_t limit)const
1207 {
1208  FC_ASSERT( _app_options, "Internal error" );
1209  const auto configured_limit = _app_options->api_limit_get_call_orders;
1210  FC_ASSERT( limit <= configured_limit,
1211  "limit can not be greater than ${configured_limit}",
1212  ("configured_limit", configured_limit) );
1213 
1214  vector<call_order_object> result;
1215  const account_id_type account = get_account_from_string(account_name_or_id)->id;
1216  const auto& call_idx = _db.get_index_type<call_order_index>().indices().get<by_account>();
1217  auto call_index_end = call_idx.end();
1218  auto call_itr = call_idx.lower_bound(boost::make_tuple(account, start));
1219  while(call_itr != call_index_end && call_itr->borrower == account && result.size() < limit)
1220  {
1221  result.push_back(*call_itr);
1222  ++call_itr;
1223  }
1224  return result;
1225 }
1226 
1227 vector<force_settlement_object> database_api::get_settle_orders(const std::string& a, uint32_t limit)const
1228 {
1229  return my->get_settle_orders( a, limit );
1230 }
1231 
1232 vector<force_settlement_object> database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const
1233 {
1234  FC_ASSERT( _app_options, "Internal error" );
1235  const auto configured_limit = _app_options->api_limit_get_settle_orders;
1236  FC_ASSERT( limit <= configured_limit,
1237  "limit can not be greater than ${configured_limit}",
1238  ("configured_limit", configured_limit) );
1239 
1240  const asset_id_type asset_a_id = get_asset_from_string(a)->id;
1241  const auto& settle_index = _db.get_index_type<force_settlement_index>().indices().get<by_expiration>();
1242  const asset_object& mia = _db.get(asset_a_id);
1243 
1244  vector<force_settlement_object> result;
1245  auto itr_min = settle_index.lower_bound(mia.get_id());
1246  auto itr_max = settle_index.upper_bound(mia.get_id());
1247  while( itr_min != itr_max && result.size() < limit )
1248  {
1249  result.emplace_back(*itr_min);
1250  ++itr_min;
1251  }
1252  return result;
1253 }
1254 
1255 vector<force_settlement_object> database_api::get_settle_orders_by_account(
1256  const std::string& account_name_or_id,
1257  force_settlement_id_type start,
1258  uint32_t limit )const
1259 {
1260  return my->get_settle_orders_by_account( account_name_or_id, start, limit);
1261 }
1262 
1263 vector<force_settlement_object> database_api_impl::get_settle_orders_by_account(
1264  const std::string& account_name_or_id,
1265  force_settlement_id_type start,
1266  uint32_t limit )const
1267 {
1268  FC_ASSERT( _app_options, "Internal error" );
1269  const auto configured_limit = _app_options->api_limit_get_settle_orders;
1270  FC_ASSERT( limit <= configured_limit,
1271  "limit can not be greater than ${configured_limit}",
1272  ("configured_limit", configured_limit) );
1273 
1274  vector<force_settlement_object> result;
1275  const account_id_type account = get_account_from_string(account_name_or_id)->id;
1276  const auto& settle_idx = _db.get_index_type<force_settlement_index>().indices().get<by_account>();
1277  auto settle_index_end = settle_idx.end();
1278  auto settle_itr = settle_idx.lower_bound(boost::make_tuple(account, start));
1279  while(settle_itr != settle_index_end && settle_itr->owner == account && result.size() < limit)
1280  {
1281  result.push_back(*settle_itr);
1282  ++settle_itr;
1283  }
1284  return result;
1285 }
1286 
1287 
1288 vector<call_order_object> database_api::get_margin_positions( const std::string account_id_or_name )const
1289 {
1290  return my->get_margin_positions( account_id_or_name );
1291 }
1292 
1293 vector<call_order_object> database_api_impl::get_margin_positions( const std::string account_id_or_name )const
1294 {
1295  try
1296  {
1297  const auto& idx = _db.get_index_type<call_order_index>();
1298  const auto& aidx = idx.indices().get<by_account>();
1299  const account_id_type id = get_account_from_string(account_id_or_name)->id;
1300  auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) );
1301  auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) );
1302  vector<call_order_object> result;
1303  while( start != end )
1304  {
1305  result.push_back(*start);
1306  ++start;
1307  }
1308  return result;
1309  } FC_CAPTURE_AND_RETHROW( (account_id_or_name) )
1310 }
1311 
1312 vector<collateral_bid_object> database_api::get_collateral_bids( const std::string& asset,
1313  uint32_t limit, uint32_t start )const
1314 {
1315  return my->get_collateral_bids( asset, limit, start );
1316 }
1317 
1318 vector<collateral_bid_object> database_api_impl::get_collateral_bids( const std::string& asset,
1319  uint32_t limit, uint32_t skip )const
1320 { try {
1321  FC_ASSERT( _app_options, "Internal error" );
1322  const auto configured_limit = _app_options->api_limit_get_collateral_bids;
1323  FC_ASSERT( limit <= configured_limit,
1324  "limit can not be greater than ${configured_limit}",
1325  ("configured_limit", configured_limit) );
1326 
1327  const asset_id_type asset_id = get_asset_from_string(asset)->id;
1328  const asset_object& swan = asset_id(_db);
1329  FC_ASSERT( swan.is_market_issued() );
1330  const asset_bitasset_data_object& bad = swan.bitasset_data(_db);
1331  const asset_object& back = bad.options.short_backing_asset(_db);
1332  const auto& idx = _db.get_index_type<collateral_bid_index>();
1333  const auto& aidx = idx.indices().get<by_price>();
1334  auto start = aidx.lower_bound( boost::make_tuple( asset_id,
1335  price::max(back.id, asset_id),
1336  collateral_bid_id_type() ) );
1337  auto end = aidx.lower_bound( boost::make_tuple( asset_id,
1338  price::min(back.id, asset_id),
1339  collateral_bid_id_type(GRAPHENE_DB_MAX_INSTANCE_ID) ) );
1340  vector<collateral_bid_object> result;
1341  while( skip-- > 0 && start != end ) { ++start; }
1342  while( start != end && limit-- > 0)
1343  {
1344  result.push_back(*start);
1345  ++start;
1346  }
1347  return result;
1348 } FC_CAPTURE_AND_RETHROW( (asset)(limit)(skip) ) }
1349 
1350 void database_api::subscribe_to_market( std::function<void(const variant&)> callback,
1351  const std::string& a, const std::string& b )
1352 {
1353  my->subscribe_to_market( callback, a, b );
1354 }
1355 
1356 void database_api_impl::subscribe_to_market( std::function<void(const variant&)> callback,
1357  const std::string& a, const std::string& b )
1358 {
1359  auto asset_a_id = get_asset_from_string(a)->id;
1360  auto asset_b_id = get_asset_from_string(b)->id;
1361 
1362  if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id);
1363  FC_ASSERT(asset_a_id != asset_b_id);
1364  _market_subscriptions[ std::make_pair(asset_a_id,asset_b_id) ] = callback;
1365 }
1366 
1367 void database_api::unsubscribe_from_market(const std::string& a, const std::string& b)
1368 {
1369  my->unsubscribe_from_market( a, b );
1370 }
1371 
1372 void database_api_impl::unsubscribe_from_market(const std::string& a, const std::string& b)
1373 {
1374  auto asset_a_id = get_asset_from_string(a)->id;
1375  auto asset_b_id = get_asset_from_string(b)->id;
1376 
1377  if(a > b) std::swap(asset_a_id,asset_b_id);
1378  FC_ASSERT(asset_a_id != asset_b_id);
1379  _market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id));
1380 }
1381 
1382 market_ticker database_api::get_ticker( const string& base, const string& quote )const
1383 {
1384  return my->get_ticker( base, quote );
1385 }
1386 
1387 market_ticker database_api_impl::get_ticker( const string& base, const string& quote, bool skip_order_book )const
1388 {
1389  FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." );
1390 
1391  const auto assets = lookup_asset_symbols( {base, quote} );
1392 
1393  FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
1394  FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
1395 
1396  auto base_id = assets[0]->id;
1397  auto quote_id = assets[1]->id;
1398  if( base_id > quote_id ) std::swap( base_id, quote_id );
1399  const auto& ticker_idx = _db.get_index_type<market_ticker_index>().indices().get<by_market>();
1400  auto itr = ticker_idx.find( std::make_tuple( base_id, quote_id ) );
1401  const fc::time_point_sec now = _db.head_block_time();
1402  if( itr != ticker_idx.end() )
1403  {
1404  order_book orders;
1405  if (!skip_order_book)
1406  {
1407  orders = get_order_book(assets[0]->symbol, assets[1]->symbol, 1);
1408  }
1409  return market_ticker(*itr, now, *assets[0], *assets[1], orders);
1410  }
1411  // if no ticker is found for this market we return an empty ticker
1412  market_ticker empty_result(now, *assets[0], *assets[1]);
1413  return empty_result;
1414 }
1415 
1416 market_volume database_api::get_24_volume( const string& base, const string& quote )const
1417 {
1418  return my->get_24_volume( base, quote );
1419 }
1420 
1421 market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const
1422 {
1423  const auto& ticker = get_ticker( base, quote, true );
1424 
1425  market_volume result;
1426  result.time = ticker.time;
1427  result.base = ticker.base;
1428  result.quote = ticker.quote;
1429  result.base_volume = ticker.base_volume;
1430  result.quote_volume = ticker.quote_volume;
1431 
1432  return result;
1433 }
1434 
1435 order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
1436 {
1437  return my->get_order_book( base, quote, limit);
1438 }
1439 
1440 order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const
1441 {
1442  FC_ASSERT( _app_options, "Internal error" );
1443  const auto configured_limit = _app_options->api_limit_get_order_book;
1444  FC_ASSERT( limit <= configured_limit,
1445  "limit can not be greater than ${configured_limit}",
1446  ("configured_limit", configured_limit) );
1447 
1448  order_book result;
1449  result.base = base;
1450  result.quote = quote;
1451 
1452  auto assets = lookup_asset_symbols( {base, quote} );
1453  FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
1454  FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
1455 
1456  auto base_id = assets[0]->id;
1457  auto quote_id = assets[1]->id;
1458  auto orders = get_limit_orders( base_id, quote_id, limit );
1459 
1460  for( const auto& o : orders )
1461  {
1462  if( o.sell_price.base.asset_id == base_id )
1463  {
1464  order ord;
1465  ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] );
1466  ord.quote = assets[1]->amount_to_string( share_type( fc::uint128_t( o.for_sale.value )
1467  * o.sell_price.quote.amount.value
1468  / o.sell_price.base.amount.value ) );
1469  ord.base = assets[0]->amount_to_string( o.for_sale );
1470  result.bids.push_back( ord );
1471  }
1472  else
1473  {
1474  order ord;
1475  ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] );
1476  ord.quote = assets[1]->amount_to_string( o.for_sale );
1477  ord.base = assets[0]->amount_to_string( share_type( fc::uint128_t( o.for_sale.value )
1478  * o.sell_price.quote.amount.value
1479  / o.sell_price.base.amount.value ) );
1480  result.asks.push_back( ord );
1481  }
1482  }
1483 
1484  return result;
1485 }
1486 
1487 vector<market_ticker> database_api::get_top_markets(uint32_t limit)const
1488 {
1489  return my->get_top_markets(limit);
1490 }
1491 
1492 vector<market_ticker> database_api_impl::get_top_markets(uint32_t limit)const
1493 {
1494  FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." );
1495 
1496  const auto configured_limit = _app_options->api_limit_get_top_markets;
1497  FC_ASSERT( limit <= configured_limit,
1498  "limit can not be greater than ${configured_limit}",
1499  ("configured_limit", configured_limit) );
1500 
1501  const auto& volume_idx = _db.get_index_type<market_ticker_index>().indices().get<by_volume>();
1502  auto itr = volume_idx.rbegin();
1503  vector<market_ticker> result;
1504  result.reserve(limit);
1505  const fc::time_point_sec now = _db.head_block_time();
1506 
1507  while( itr != volume_idx.rend() && result.size() < limit)
1508  {
1509  const asset_object base = itr->base(_db);
1510  const asset_object quote = itr->quote(_db);
1511  order_book orders;
1512  orders = get_order_book(base.symbol, quote.symbol, 1);
1513 
1514  result.emplace_back(market_ticker(*itr, now, base, quote, orders));
1515  ++itr;
1516  }
1517  return result;
1518 }
1519 
1520 vector<market_trade> database_api::get_trade_history( const string& base,
1521  const string& quote,
1522  fc::time_point_sec start,
1523  fc::time_point_sec stop,
1524  unsigned limit )const
1525 {
1526  return my->get_trade_history( base, quote, start, stop, limit );
1527 }
1528 
1529 vector<market_trade> database_api_impl::get_trade_history( const string& base,
1530  const string& quote,
1531  fc::time_point_sec start,
1532  fc::time_point_sec stop,
1533  unsigned limit )const
1534 {
1535  FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." );
1536 
1537  const auto configured_limit = _app_options->api_limit_get_trade_history;
1538  FC_ASSERT( limit <= configured_limit,
1539  "limit can not be greater than ${configured_limit}",
1540  ("configured_limit", configured_limit) );
1541 
1542  auto assets = lookup_asset_symbols( {base, quote} );
1543  FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
1544  FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
1545 
1546  auto base_id = assets[0]->id;
1547  auto quote_id = assets[1]->id;
1548 
1549  if( base_id > quote_id ) std::swap( base_id, quote_id );
1550 
1551  if ( start.sec_since_epoch() == 0 )
1553 
1554  uint32_t count = 0;
1555  const auto& history_idx = _db.get_index_type<market_history::history_index>().indices().get<by_market_time>();
1556  auto itr = history_idx.lower_bound( std::make_tuple( base_id, quote_id, start ) );
1557  vector<market_trade> result;
1558 
1559  while( itr != history_idx.end() && count < limit
1560  && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )
1561  {
1562  {
1563  market_trade trade;
1564 
1565  if( assets[0]->id == itr->op.receives.asset_id )
1566  {
1567  trade.amount = assets[1]->amount_to_string( itr->op.pays );
1568  trade.value = assets[0]->amount_to_string( itr->op.receives );
1569  }
1570  else
1571  {
1572  trade.amount = assets[1]->amount_to_string( itr->op.receives );
1573  trade.value = assets[0]->amount_to_string( itr->op.pays );
1574  }
1575 
1576  trade.date = itr->time;
1577  trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] );
1578 
1579  if( itr->op.is_maker )
1580  {
1581  trade.sequence = -itr->key.sequence;
1582  trade.side1_account_id = itr->op.account_id;
1583  if(itr->op.receives.asset_id == assets[0]->id)
1584  trade.type = "sell";
1585  else
1586  trade.type = "buy";
1587  }
1588  else
1589  trade.side2_account_id = itr->op.account_id;
1590 
1591  auto next_itr = std::next(itr);
1592  // Trades are usually tracked in each direction, exception: for global settlement only one side is recorded
1593  if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id
1594  && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )
1595  { // next_itr now could be the other direction // FIXME not 100% sure
1596  if( next_itr->op.is_maker )
1597  {
1598  trade.sequence = -next_itr->key.sequence;
1599  trade.side1_account_id = next_itr->op.account_id;
1600  if(next_itr->op.receives.asset_id == assets[0]->id)
1601  trade.type = "sell";
1602  else
1603  trade.type = "buy";
1604  }
1605  else
1606  trade.side2_account_id = next_itr->op.account_id;
1607  // skip the other direction
1608  itr = next_itr;
1609  }
1610 
1611  result.push_back( trade );
1612  ++count;
1613  }
1614 
1615  ++itr;
1616  }
1617 
1618  return result;
1619 }
1620 
1622  const string& base,
1623  const string& quote,
1624  int64_t start,
1625  fc::time_point_sec stop,
1626  unsigned limit )const
1627 {
1628  return my->get_trade_history_by_sequence( base, quote, start, stop, limit );
1629 }
1630 
1632  const string& base,
1633  const string& quote,
1634  int64_t start,
1635  fc::time_point_sec stop,
1636  unsigned limit )const
1637 {
1638  FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." );
1639 
1640  const auto configured_limit = _app_options->api_limit_get_trade_history_by_sequence;
1641  FC_ASSERT( limit <= configured_limit,
1642  "limit can not be greater than ${configured_limit}",
1643  ("configured_limit", configured_limit) );
1644 
1645  FC_ASSERT( start >= 0 );
1646  int64_t start_seq = -start;
1647 
1648  auto assets = lookup_asset_symbols( {base, quote} );
1649  FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
1650  FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
1651 
1652  auto base_id = assets[0]->id;
1653  auto quote_id = assets[1]->id;
1654 
1655  if( base_id > quote_id ) std::swap( base_id, quote_id );
1656  const auto& history_idx = _db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();
1657  history_key hkey;
1658  hkey.base = base_id;
1659  hkey.quote = quote_id;
1660  hkey.sequence = start_seq;
1661 
1662  uint32_t count = 0;
1663  auto itr = history_idx.lower_bound( hkey );
1664  vector<market_trade> result;
1665 
1666  while( itr != history_idx.end() && count < limit
1667  && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )
1668  {
1669  if( itr->key.sequence == start_seq ) // found the key, should skip this and the other direction if found
1670  {
1671  auto next_itr = std::next(itr);
1672  if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id
1673  && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )
1674  { // next_itr now could be the other direction // FIXME not 100% sure
1675  // skip the other direction
1676  itr = next_itr;
1677  }
1678  }
1679  else
1680  {
1681  market_trade trade;
1682 
1683  if( assets[0]->id == itr->op.receives.asset_id )
1684  {
1685  trade.amount = assets[1]->amount_to_string( itr->op.pays );
1686  trade.value = assets[0]->amount_to_string( itr->op.receives );
1687  }
1688  else
1689  {
1690  trade.amount = assets[1]->amount_to_string( itr->op.receives );
1691  trade.value = assets[0]->amount_to_string( itr->op.pays );
1692  }
1693 
1694  trade.date = itr->time;
1695  trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] );
1696 
1697  if( itr->op.is_maker )
1698  {
1699  trade.sequence = -itr->key.sequence;
1700  trade.side1_account_id = itr->op.account_id;
1701  if(itr->op.receives.asset_id == assets[0]->id)
1702  trade.type = "sell";
1703  else
1704  trade.type = "buy";
1705  }
1706  else
1707  trade.side2_account_id = itr->op.account_id;
1708 
1709  auto next_itr = std::next(itr);
1710  // Trades are usually tracked in each direction, exception: for global settlement only one side is recorded
1711  if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id
1712  && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )
1713  { // next_itr now could be the other direction // FIXME not 100% sure
1714  if( next_itr->op.is_maker )
1715  {
1716  trade.sequence = -next_itr->key.sequence;
1717  trade.side1_account_id = next_itr->op.account_id;
1718  if(next_itr->op.receives.asset_id == assets[0]->id)
1719  trade.type = "sell";
1720  else
1721  trade.type = "buy";
1722  }
1723  else
1724  trade.side2_account_id = next_itr->op.account_id;
1725  // skip the other direction
1726  itr = next_itr;
1727  }
1728 
1729  result.push_back( trade );
1730  ++count;
1731  }
1732 
1733  ++itr;
1734  }
1735 
1736  return result;
1737 }
1738 
1740 // //
1741 // Liquidity pools //
1742 // //
1744 
1745 vector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_a(
1746  std::string asset_symbol_or_id,
1747  optional<uint32_t> limit,
1748  optional<liquidity_pool_id_type> start_id )const
1749 {
1750  return my->get_liquidity_pools_by_asset_a(
1751  asset_symbol_or_id,
1752  limit,
1753  start_id );
1754 }
1755 
1757  std::string asset_symbol_or_id,
1758  optional<uint32_t> limit,
1759  optional<liquidity_pool_id_type> start_id )const
1760 {
1761  return get_liquidity_pools_by_asset_x<by_asset_a>(
1762  asset_symbol_or_id,
1763  limit,
1764  start_id );
1765 }
1766 
1767 vector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_b(
1768  std::string asset_symbol_or_id,
1769  optional<uint32_t> limit,
1770  optional<liquidity_pool_id_type> start_id )const
1771 {
1772  return my->get_liquidity_pools_by_asset_b(
1773  asset_symbol_or_id,
1774  limit,
1775  start_id );
1776 }
1777 
1779  std::string asset_symbol_or_id,
1780  optional<uint32_t> limit,
1781  optional<liquidity_pool_id_type> start_id )const
1782 {
1783  return get_liquidity_pools_by_asset_x<by_asset_b>(
1784  asset_symbol_or_id,
1785  limit,
1786  start_id );
1787 }
1788 
1790  std::string asset_symbol_or_id_a,
1791  std::string asset_symbol_or_id_b,
1792  optional<uint32_t> limit,
1793  optional<liquidity_pool_id_type> start_id )const
1794 {
1795  return my->get_liquidity_pools_by_both_assets(
1796  asset_symbol_or_id_a,
1797  asset_symbol_or_id_b,
1798  limit,
1799  start_id );
1800 }
1801 
1803  std::string asset_symbol_or_id_a,
1804  std::string asset_symbol_or_id_b,
1805  optional<uint32_t> olimit,
1806  optional<liquidity_pool_id_type> ostart_id )const
1807 {
1808  uint32_t limit = olimit.valid() ? *olimit : 101;
1809 
1810  FC_ASSERT( _app_options, "Internal error" );
1811  const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
1812  FC_ASSERT( limit <= configured_limit,
1813  "limit can not be greater than ${configured_limit}",
1814  ("configured_limit", configured_limit) );
1815 
1816  vector<liquidity_pool_object> results;
1817 
1818  asset_id_type asset_id_a = get_asset_from_string(asset_symbol_or_id_a)->id;
1819  asset_id_type asset_id_b = get_asset_from_string(asset_symbol_or_id_b)->id;
1820  if( asset_id_a > asset_id_b )
1821  std::swap( asset_id_a, asset_id_b );
1822 
1823  liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();
1824 
1825  const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<by_asset_ab>();
1826  auto lower_itr = idx.lower_bound( std::make_tuple( asset_id_a, asset_id_b, start_id ) );
1827  auto upper_itr = idx.upper_bound( std::make_tuple( asset_id_a, asset_id_b ) );
1828 
1829  results.reserve( limit );
1830  uint32_t count = 0;
1831  for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
1832  {
1833  results.emplace_back( *lower_itr );
1834  }
1835 
1836  return results;
1837 }
1838 
1839 vector<optional<liquidity_pool_object>> database_api::get_liquidity_pools_by_share_asset(
1840  const vector<std::string>& asset_symbols_or_ids,
1841  optional<bool> subscribe )const
1842 {
1843  return my->get_liquidity_pools_by_share_asset(
1844  asset_symbols_or_ids,
1845  subscribe );
1846 }
1847 
1848 vector<optional<liquidity_pool_object>> database_api_impl::get_liquidity_pools_by_share_asset(
1849  const vector<std::string>& asset_symbols_or_ids,
1850  optional<bool> subscribe )const
1851 {
1852  FC_ASSERT( _app_options, "Internal error" );
1853  const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
1854  FC_ASSERT( asset_symbols_or_ids.size() <= configured_limit,
1855  "size of the querying list can not be greater than ${configured_limit}",
1856  ("configured_limit", configured_limit) );
1857 
1858  bool to_subscribe = get_whether_to_subscribe( subscribe );
1859  vector<optional<liquidity_pool_object>> result; result.reserve(asset_symbols_or_ids.size());
1860  std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),
1861  [this,to_subscribe](std::string id_or_name) -> optional<liquidity_pool_object> {
1862 
1863  const asset_object* asset_obj = get_asset_from_string( id_or_name, false );
1864  if( asset_obj == nullptr || !asset_obj->is_liquidity_pool_share_asset() )
1865  return {};
1866  const liquidity_pool_object& lp_obj = (*asset_obj->for_liquidity_pool)(_db);
1867  if( to_subscribe )
1868  subscribe_to_item( lp_obj.id );
1869  return lp_obj;
1870  });
1871  return result;
1872 }
1873 
1875 // //
1876 // Witnesses //
1877 // //
1879 
1880 vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const
1881 {
1882  return my->get_witnesses( witness_ids );
1883 }
1884 
1885 vector<optional<witness_object>> database_api_impl::get_witnesses(const vector<witness_id_type>& witness_ids)const
1886 {
1887  vector<optional<witness_object>> result; result.reserve(witness_ids.size());
1888  std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result),
1889  [this](witness_id_type id) -> optional<witness_object> {
1890  if(auto o = _db.find(id))
1891  return *o;
1892  return {};
1893  });
1894  return result;
1895 }
1896 
1897 fc::optional<witness_object> database_api::get_witness_by_account(const std::string account_id_or_name)const
1898 {
1899  return my->get_witness_by_account( account_id_or_name );
1900 }
1901 
1903 {
1904  const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();
1905  const account_id_type account = get_account_from_string(account_id_or_name)->id;
1906  auto itr = idx.find(account);
1907  if( itr != idx.end() )
1908  return *itr;
1909  return {};
1910 }
1911 
1912 map<string, witness_id_type> database_api::lookup_witness_accounts( const string& lower_bound_name,
1913  uint32_t limit )const
1914 {
1915  return my->lookup_witness_accounts( lower_bound_name, limit );
1916 }
1917 
1918 map<string, witness_id_type> database_api_impl::lookup_witness_accounts( const string& lower_bound_name,
1919  uint32_t limit )const
1920 {
1921  FC_ASSERT( _app_options, "Internal error" );
1922  const auto configured_limit = _app_options->api_limit_lookup_witness_accounts;
1923  FC_ASSERT( limit <= configured_limit,
1924  "limit can not be greater than ${configured_limit}",
1925  ("configured_limit", configured_limit) );
1926 
1927  const auto& witnesses_by_id = _db.get_index_type<witness_index>().indices().get<by_id>();
1928 
1929  // we want to order witnesses by account name, but that name is in the account object
1930  // so the witness_index doesn't have a quick way to access it.
1931  // get all the names and look them all up, sort them, then figure out what
1932  // records to return. This could be optimized, but we expect the
1933  // number of witnesses to be few and the frequency of calls to be rare
1934  std::map<std::string, witness_id_type> witnesses_by_account_name;
1935  for (const witness_object& witness : witnesses_by_id)
1936  if (auto account_iter = _db.find(witness.witness_account))
1937  if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
1938  witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));
1939 
1940  auto end_iter = witnesses_by_account_name.begin();
1941  while (end_iter != witnesses_by_account_name.end() && limit--)
1942  ++end_iter;
1943  witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());
1944  return witnesses_by_account_name;
1945 }
1946 
1948 {
1949  return my->get_witness_count();
1950 }
1951 
1953 {
1954  return _db.get_index_type<witness_index>().indices().size();
1955 }
1956 
1958 // //
1959 // Committee members //
1960 // //
1962 
1963 vector<optional<committee_member_object>> database_api::get_committee_members(
1964  const vector<committee_member_id_type>& committee_member_ids )const
1965 {
1966  return my->get_committee_members( committee_member_ids );
1967 }
1968 
1969 vector<optional<committee_member_object>> database_api_impl::get_committee_members(
1970  const vector<committee_member_id_type>& committee_member_ids )const
1971 {
1972  vector<optional<committee_member_object>> result; result.reserve(committee_member_ids.size());
1973  std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result),
1974  [this](committee_member_id_type id) -> optional<committee_member_object> {
1975  if(auto o = _db.find(id))
1976  return *o;
1977  return {};
1978  });
1979  return result;
1980 }
1981 
1983  const std::string account_id_or_name )const
1984 {
1985  return my->get_committee_member_by_account( account_id_or_name );
1986 }
1987 
1989  const std::string account_id_or_name )const
1990 {
1991  const auto& idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
1992  const account_id_type account = get_account_from_string(account_id_or_name)->id;
1993  auto itr = idx.find(account);
1994  if( itr != idx.end() )
1995  return *itr;
1996  return {};
1997 }
1998 
1999 map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(
2000  const string& lower_bound_name, uint32_t limit )const
2001 {
2002  return my->lookup_committee_member_accounts( lower_bound_name, limit );
2003 }
2004 
2005 map<string, committee_member_id_type> database_api_impl::lookup_committee_member_accounts(
2006  const string& lower_bound_name, uint32_t limit )const
2007 {
2008  FC_ASSERT( _app_options, "Internal error" );
2009  const auto configured_limit = _app_options->api_limit_lookup_committee_member_accounts;
2010  FC_ASSERT( limit <= configured_limit,
2011  "limit can not be greater than ${configured_limit}",
2012  ("configured_limit", configured_limit) );
2013 
2014  const auto& committee_members_by_id = _db.get_index_type<committee_member_index>().indices().get<by_id>();
2015 
2016  // we want to order committee_members by account name, but that name is in the account object
2017  // so the committee_member_index doesn't have a quick way to access it.
2018  // get all the names and look them all up, sort them, then figure out what
2019  // records to return. This could be optimized, but we expect the
2020  // number of committee_members to be few and the frequency of calls to be rare
2021  std::map<std::string, committee_member_id_type> committee_members_by_account_name;
2022  for (const committee_member_object& committee_member : committee_members_by_id)
2023  if (auto account_iter = _db.find(committee_member.committee_member_account))
2024  if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
2025  committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));
2026 
2027  auto end_iter = committee_members_by_account_name.begin();
2028  while (end_iter != committee_members_by_account_name.end() && limit--)
2029  ++end_iter;
2030  committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());
2031  return committee_members_by_account_name;
2032 }
2033 
2035 {
2036  return my->get_committee_count();
2037 }
2038 
2040 {
2041  return _db.get_index_type<committee_member_index>().indices().size();
2042 }
2043 
2044 
2046 // //
2047 // Workers //
2048 // //
2050 
2051 vector<worker_object> database_api::get_all_workers( const optional<bool> is_expired )const
2052 {
2053  return my->get_all_workers( is_expired );
2054 }
2055 
2056 vector<worker_object> database_api_impl::get_all_workers( const optional<bool> is_expired )const
2057 {
2058  vector<worker_object> result;
2059 
2060  if( !is_expired.valid() ) // query for all workers
2061  {
2062  const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_id>();
2063  result.reserve( workers_idx.size() );
2064  for( const auto& w : workers_idx )
2065  {
2066  result.push_back( w );
2067  }
2068  }
2069  else // query for workers that are expired only or not expired only
2070  {
2071  const time_point_sec now = _db.head_block_time();
2072  const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_end_date>();
2073  auto itr = *is_expired ? workers_idx.begin() : workers_idx.lower_bound( now );
2074  auto end = *is_expired ? workers_idx.upper_bound( now ) : workers_idx.end();
2075  for( ; itr != end; ++itr )
2076  {
2077  result.push_back( *itr );
2078  }
2079  }
2080 
2081  return result;
2082 }
2083 
2084 vector<worker_object> database_api::get_workers_by_account(const std::string account_id_or_name)const
2085 {
2086  return my->get_workers_by_account( account_id_or_name );
2087 }
2088 
2089 vector<worker_object> database_api_impl::get_workers_by_account(const std::string account_id_or_name)const
2090 {
2091  vector<worker_object> result;
2092  const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_account>();
2093 
2094  const account_id_type account = get_account_from_string(account_id_or_name)->id;
2095  auto range = workers_idx.equal_range(account);
2096  for(auto itr = range.first; itr != range.second; ++itr)
2097  {
2098  result.push_back( *itr );
2099  }
2100  return result;
2101 }
2102 
2104 {
2105  return my->get_worker_count();
2106 }
2107 
2109 {
2110  return _db.get_index_type<worker_index>().indices().size();
2111 }
2112 
2113 
2114 
2116 // //
2117 // Votes //
2118 // //
2120 
2121 vector<variant> database_api::lookup_vote_ids( const vector<vote_id_type>& votes )const
2122 {
2123  return my->lookup_vote_ids( votes );
2124 }
2125 
2126 vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>& votes )const
2127 {
2128  FC_ASSERT( _app_options, "Internal error" );
2129  const auto configured_limit = _app_options->api_limit_lookup_vote_ids;
2130  FC_ASSERT( votes.size() <= configured_limit,
2131  "Number of querying votes can not be greater than ${configured_limit}",
2132  ("configured_limit", configured_limit) );
2133 
2134  const auto& witness_idx = _db.get_index_type<witness_index>().indices().get<by_vote_id>();
2135  const auto& committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
2136  const auto& for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
2137  const auto& against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
2138 
2139  vector<variant> result;
2140  result.reserve( votes.size() );
2141  for( auto id : votes )
2142  {
2143  switch( id.type() )
2144  {
2146  {
2147  auto itr = committee_idx.find( id );
2148  if( itr != committee_idx.end() )
2149  result.emplace_back( variant( *itr, 2 ) ); // Depth of committee_member_object is 1, add 1 to be safe
2150  else
2151  result.emplace_back( variant() );
2152  break;
2153  }
2154  case vote_id_type::witness:
2155  {
2156  auto itr = witness_idx.find( id );
2157  if( itr != witness_idx.end() )
2158  result.emplace_back( variant( *itr, 2 ) ); // Depth of witness_object is 1, add 1 here to be safe
2159  else
2160  result.emplace_back( variant() );
2161  break;
2162  }
2163  case vote_id_type::worker:
2164  {
2165  auto itr = for_worker_idx.find( id );
2166  if( itr != for_worker_idx.end() ) {
2167  result.emplace_back( variant( *itr, 4 ) ); // Depth of worker_object is 3, add 1 here to be safe.
2168  // If we want to extract the balance object inside,
2169  // need to increase this value
2170  }
2171  else {
2172  auto itr = against_worker_idx.find( id );
2173  if( itr != against_worker_idx.end() ) {
2174  result.emplace_back( variant( *itr, 4 ) ); // Depth of worker_object is 3, add 1 here to be safe.
2175  // If we want to extract the balance object inside,
2176  // need to increase this value
2177  }
2178  else {
2179  result.emplace_back( variant() );
2180  }
2181  }
2182  break;
2183  }
2184  case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings
2185  default:
2186  FC_CAPTURE_AND_THROW( fc::out_of_range_exception, (id) );
2187  }
2188  }
2189  return result;
2190 }
2191 
2193 // //
2194 // Authority / validation //
2195 // //
2197 
2198 std::string database_api::get_transaction_hex(const signed_transaction& trx)const
2199 {
2200  return my->get_transaction_hex( trx );
2201 }
2202 
2203 std::string database_api_impl::get_transaction_hex(const signed_transaction& trx)const
2204 {
2205  return fc::to_hex(fc::raw::pack(trx));
2206 }
2207 
2209  const signed_transaction &trx) const
2210 {
2211  return my->get_transaction_hex_without_sig(trx);
2212 }
2213 
2215  const signed_transaction &trx) const
2216 {
2217  return fc::to_hex(fc::raw::pack(static_cast<transaction>(trx)));
2218 }
2219 
2220 set<public_key_type> database_api::get_required_signatures( const signed_transaction& trx,
2221  const flat_set<public_key_type>& available_keys )const
2222 {
2223  return my->get_required_signatures( trx, available_keys );
2224 }
2225 
2226 set<public_key_type> database_api_impl::get_required_signatures( const signed_transaction& trx,
2227  const flat_set<public_key_type>& available_keys )const
2228 {
2229  auto chain_time = _db.head_block_time();
2230  bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );
2231  bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );
2232  auto result = trx.get_required_signatures( _db.get_chain_id(),
2233  available_keys,
2234  [&]( account_id_type id ){ return &id(_db).active; },
2235  [&]( account_id_type id ){ return &id(_db).owner; },
2236  allow_non_immediate_owner,
2237  ignore_custom_op_reqd_auths,
2239  return result;
2240 }
2241 
2242 set<public_key_type> database_api::get_potential_signatures( const signed_transaction& trx )const
2243 {
2244  return my->get_potential_signatures( trx );
2245 }
2246 set<address> database_api::get_potential_address_signatures( const signed_transaction& trx )const
2247 {
2248  return my->get_potential_address_signatures( trx );
2249 }
2250 
2251 set<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const
2252 {
2253  auto chain_time = _db.head_block_time();
2254  bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );
2255  bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );
2256 
2257  set<public_key_type> result;
2258  auto get_active = [this, &result]( account_id_type id ){
2259  const auto& auth = id( _db ).active;
2260  for( const auto& k : auth.get_keys() )
2261  result.insert( k );
2262  return &auth;
2263  };
2264  auto get_owner = [this, &result]( account_id_type id ){
2265  const auto& auth = id( _db ).owner;
2266  for( const auto& k : auth.get_keys() )
2267  result.insert( k );
2268  return &auth;
2269  };
2270 
2271  trx.get_required_signatures( _db.get_chain_id(),
2272  flat_set<public_key_type>(),
2273  get_active, get_owner,
2274  allow_non_immediate_owner,
2275  ignore_custom_op_reqd_auths,
2277 
2278  // Insert keys in required "other" authories
2279  flat_set<account_id_type> required_active;
2280  flat_set<account_id_type> required_owner;
2281  vector<authority> other;
2282  trx.get_required_authorities( required_active, required_owner, other, ignore_custom_op_reqd_auths );
2283  for( const auto& auth : other )
2284  for( const auto& key : auth.get_keys() )
2285  result.insert( key );
2286 
2287  return result;
2288 }
2289 
2290 set<address> database_api_impl::get_potential_address_signatures( const signed_transaction& trx )const
2291 {
2292  auto chain_time = _db.head_block_time();
2293  bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );
2294  bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );
2295 
2296  set<address> result;
2297  auto get_active = [this, &result]( account_id_type id ){
2298  const auto& auth = id( _db ).active;
2299  for( const auto& k : auth.get_addresses() )
2300  result.insert( k );
2301  return &auth;
2302  };
2303  auto get_owner = [this, &result]( account_id_type id ) {
2304  const auto& auth = id( _db ).owner;
2305  for (const auto& k : auth.get_addresses())
2306  result.insert( k );
2307  return &auth;
2308  };
2309 
2310  trx.get_required_signatures( _db.get_chain_id(),
2311  flat_set<public_key_type>(),
2312  get_active, get_owner,
2313  allow_non_immediate_owner,
2314  ignore_custom_op_reqd_auths,
2316  return result;
2317 }
2318 
2319 bool database_api::verify_authority( const signed_transaction& trx )const
2320 {
2321  return my->verify_authority( trx );
2322 }
2323 
2324 bool database_api_impl::verify_authority( const signed_transaction& trx )const
2325 {
2326  bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME );
2327  trx.verify_authority( _db.get_chain_id(),
2328  [this]( account_id_type id ){ return &id(_db).active; },
2329  [this]( account_id_type id ){ return &id(_db).owner; },
2330  [this]( account_id_type id, const operation& op, rejected_predicate_map* rejects ) {
2331  return _db.get_viable_custom_authorities(id, op, rejects); },
2332  allow_non_immediate_owner,
2334  return true;
2335 }
2336 
2337 bool database_api::verify_account_authority( const string& account_name_or_id,
2338  const flat_set<public_key_type>& signers )const
2339 {
2340  return my->verify_account_authority( account_name_or_id, signers );
2341 }
2342 
2343 bool database_api_impl::verify_account_authority( const string& account_name_or_id,
2344  const flat_set<public_key_type>& keys )const
2345 {
2346  // create a dummy transfer
2347  transfer_operation op;
2348  op.from = get_account_from_string(account_name_or_id)->id;
2349  std::vector<operation> ops;
2350  ops.emplace_back(op);
2351 
2352  try
2353  {
2355  [this]( account_id_type id ){ return &id(_db).active; },
2356  [this]( account_id_type id ){ return &id(_db).owner; },
2357  // Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op
2358  [](auto, auto, auto*) { return vector<authority>(); },
2359  true, MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(_db.head_block_time()) );
2360  }
2361  catch (fc::exception& ex)
2362  {
2363  return false;
2364  }
2365 
2366  return true;
2367 }
2368 
2369 processed_transaction database_api::validate_transaction( const signed_transaction& trx )const
2370 {
2371  return my->validate_transaction( trx );
2372 }
2373 
2375 {
2376  return _db.validate_transaction(trx);
2377 }
2378 
2379 vector< fc::variant > database_api::get_required_fees( const vector<operation>& ops,
2380  const std::string& asset_id_or_symbol )const
2381 {
2382  return my->get_required_fees( ops, asset_id_or_symbol );
2383 }
2384 
2390 {
2392  const fee_schedule& _current_fee_schedule,
2393  const price& _core_exchange_rate,
2394  uint32_t _max_recursion
2395  )
2396  : current_fee_schedule(_current_fee_schedule),
2397  core_exchange_rate(_core_exchange_rate),
2398  max_recursion(_max_recursion)
2399  {}
2400 
2402  {
2404  {
2405  return set_proposal_create_op_fees( op );
2406  }
2407  else
2408  {
2409  asset fee = current_fee_schedule.set_fee( op, core_exchange_rate );
2410  fc::variant result;
2412  return result;
2413  }
2414  }
2415 
2417  {
2418  proposal_create_operation& op = proposal_create_op.get<proposal_create_operation>();
2419  std::pair< asset, fc::variants > result;
2420  for( op_wrapper& prop_op : op.proposed_ops )
2421  {
2422  FC_ASSERT( current_recursion < max_recursion );
2423  ++current_recursion;
2424  result.second.push_back( set_op_fees( prop_op.op ) );
2425  --current_recursion;
2426  }
2427  // we need to do this on the boxed version, which is why we use
2428  // two mutually recursive functions instead of a visitor
2429  result.first = current_fee_schedule.set_fee( proposal_create_op, core_exchange_rate );
2430  fc::variant vresult;
2431  fc::to_variant( result, vresult, GRAPHENE_NET_MAX_NESTED_OBJECTS );
2432  return vresult;
2433  }
2434 
2435  const fee_schedule& current_fee_schedule;
2437  uint32_t max_recursion;
2438  uint32_t current_recursion = 0;
2439 };
2440 
2441 vector< fc::variant > database_api_impl::get_required_fees( const vector<operation>& ops,
2442  const std::string& asset_id_or_symbol )const
2443 {
2444  vector< operation > _ops = ops;
2445  //
2446  // we copy the ops because we need to mutate an operation to reliably
2447  // determine its fee, see #435
2448  //
2449 
2450  vector< fc::variant > result;
2451  result.reserve(ops.size());
2452  const asset_object& a = *get_asset_from_string(asset_id_or_symbol);
2453  get_required_fees_helper helper(
2457  for( operation& op : _ops )
2458  {
2459  result.push_back( helper.set_op_fees( op ) );
2460  }
2461  return result;
2462 }
2463 
2465 // //
2466 // Proposed transactions //
2467 // //
2469 
2470 vector<proposal_object> database_api::get_proposed_transactions( const std::string account_id_or_name )const
2471 {
2472  return my->get_proposed_transactions( account_id_or_name );
2473 }
2474 
2475 vector<proposal_object> database_api_impl::get_proposed_transactions( const std::string account_id_or_name )const
2476 {
2477  // api_helper_indexes plugin is required for accessing the secondary index
2479  "api_helper_indexes plugin is not enabled on this server." );
2480 
2481  const auto& proposal_idx = _db.get_index_type< primary_index< proposal_index > >();
2482  const auto& proposals_by_account = proposal_idx.get_secondary_index<graphene::chain::required_approval_index>();
2483 
2484  vector<proposal_object> result;
2485  const account_id_type id = get_account_from_string(account_id_or_name)->id;
2486 
2487  auto required_approvals_itr = proposals_by_account._account_to_proposals.find( id );
2488  if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )
2489  {
2490  result.reserve( required_approvals_itr->second.size() );
2491  for( auto proposal_id : required_approvals_itr->second )
2492  {
2493  result.push_back( proposal_id(_db) );
2494  }
2495  }
2496  return result;
2497 }
2498 
2500 // //
2501 // Blinded balances //
2502 // //
2504 
2505 vector<blinded_balance_object> database_api::get_blinded_balances(
2506  const flat_set<commitment_type>& commitments )const
2507 {
2508  return my->get_blinded_balances( commitments );
2509 }
2510 
2511 vector<blinded_balance_object> database_api_impl::get_blinded_balances(
2512  const flat_set<commitment_type>& commitments )const
2513 {
2514  vector<blinded_balance_object> result; result.reserve(commitments.size());
2515  const auto& bal_idx = _db.get_index_type<blinded_balance_index>();
2516  const auto& by_commitment_idx = bal_idx.indices().get<by_commitment>();
2517  for( const auto& c : commitments )
2518  {
2519  auto itr = by_commitment_idx.find( c );
2520  if( itr != by_commitment_idx.end() )
2521  result.push_back( *itr );
2522  }
2523  return result;
2524 }
2525 
2527 // //
2528 // Withdrawals //
2529 // //
2531 
2532 vector<withdraw_permission_object> database_api::get_withdraw_permissions_by_giver(
2533  const std::string account_id_or_name,
2534  withdraw_permission_id_type start,
2535  uint32_t limit)const
2536 {
2537  return my->get_withdraw_permissions_by_giver( account_id_or_name, start, limit );
2538 }
2539 
2540 vector<withdraw_permission_object> database_api_impl::get_withdraw_permissions_by_giver(
2541  const std::string account_id_or_name,
2542  withdraw_permission_id_type start,
2543  uint32_t limit)const
2544 {
2545  FC_ASSERT( _app_options, "Internal error" );
2546  const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_giver;
2547  FC_ASSERT( limit <= configured_limit,
2548  "limit can not be greater than ${configured_limit}",
2549  ("configured_limit", configured_limit) );
2550 
2551  vector<withdraw_permission_object> result;
2552 
2553  const auto& withdraw_idx = _db.get_index_type<withdraw_permission_index>().indices().get<by_from>();
2554  auto withdraw_index_end = withdraw_idx.end();
2555  const account_id_type account = get_account_from_string(account_id_or_name)->id;
2556  auto withdraw_itr = withdraw_idx.lower_bound(boost::make_tuple(account, start));
2557  while( withdraw_itr != withdraw_index_end && withdraw_itr->withdraw_from_account == account
2558  && result.size() < limit )
2559  {
2560  result.push_back(*withdraw_itr);
2561  ++withdraw_itr;
2562  }
2563  return result;
2564 }
2565 
2566 vector<withdraw_permission_object> database_api::get_withdraw_permissions_by_recipient(
2567  const std::string account_id_or_name,
2568  withdraw_permission_id_type start,
2569  uint32_t limit)const
2570 {
2571  return my->get_withdraw_permissions_by_recipient( account_id_or_name, start, limit );
2572 }
2573 
2575  const std::string account_id_or_name,
2576  withdraw_permission_id_type start,
2577  uint32_t limit)const
2578 {
2579  FC_ASSERT( _app_options, "Internal error" );
2580  const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_recipient;
2581  FC_ASSERT( limit <= configured_limit,
2582  "limit can not be greater than ${configured_limit}",
2583  ("configured_limit", configured_limit) );
2584 
2585  vector<withdraw_permission_object> result;
2586 
2587  const auto& withdraw_idx = _db.get_index_type<withdraw_permission_index>().indices().get<by_authorized>();
2588  auto withdraw_index_end = withdraw_idx.end();
2589  const account_id_type account = get_account_from_string(account_id_or_name)->id;
2590  auto withdraw_itr = withdraw_idx.lower_bound(boost::make_tuple(account, start));
2591  while(withdraw_itr != withdraw_index_end && withdraw_itr->authorized_account == account && result.size() < limit)
2592  {
2593  result.push_back(*withdraw_itr);
2594  ++withdraw_itr;
2595  }
2596  return result;
2597 }
2598 
2600 // //
2601 // HTLC //
2602 // //
2604 
2605 optional<htlc_object> database_api::get_htlc( htlc_id_type id, optional<bool> subscribe )const
2606 {
2607  return my->get_htlc( id, subscribe );
2608 }
2609 
2610 fc::optional<htlc_object> database_api_impl::get_htlc( htlc_id_type id, optional<bool> subscribe )const
2611 {
2612  auto obj = get_objects( { id }, subscribe ).front();
2613  if ( !obj.is_null() )
2614  {
2615  return fc::optional<htlc_object>(obj.template as<htlc_object>(GRAPHENE_MAX_NESTED_OBJECTS));
2616  }
2617  return fc::optional<htlc_object>();
2618 }
2619 
2620 vector<htlc_object> database_api::get_htlc_by_from( const std::string account_id_or_name,
2621  htlc_id_type start, uint32_t limit )const
2622 {
2623  return my->get_htlc_by_from(account_id_or_name, start, limit);
2624 }
2625 
2626 vector<htlc_object> database_api_impl::get_htlc_by_from( const std::string account_id_or_name,
2627  htlc_id_type start, uint32_t limit ) const
2628 {
2629  FC_ASSERT( _app_options, "Internal error" );
2630  const auto configured_limit = _app_options->api_limit_get_htlc_by;
2631  FC_ASSERT( limit <= configured_limit,
2632  "limit can not be greater than ${configured_limit}",
2633  ("configured_limit", configured_limit) );
2634 
2635  vector<htlc_object> result;
2636 
2637  const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_from_id >();
2638  auto htlc_index_end = htlc_idx.end();
2639  const account_id_type account = get_account_from_string(account_id_or_name)->id;
2640  auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));
2641 
2642  while(htlc_itr != htlc_index_end && htlc_itr->transfer.from == account && result.size() < limit)
2643  {
2644  result.push_back(*htlc_itr);
2645  ++htlc_itr;
2646  }
2647  return result;
2648 }
2649 
2650 vector<htlc_object> database_api::get_htlc_by_to( const std::string account_id_or_name,
2651  htlc_id_type start, uint32_t limit )const
2652 {
2653  return my->get_htlc_by_to(account_id_or_name, start, limit);
2654 }
2655 
2656 vector<htlc_object> database_api_impl::get_htlc_by_to( const std::string account_id_or_name,
2657  htlc_id_type start, uint32_t limit ) const
2658 {
2659  FC_ASSERT( _app_options, "Internal error" );
2660  const auto configured_limit = _app_options->api_limit_get_htlc_by;
2661  FC_ASSERT( limit <= configured_limit,
2662  "limit can not be greater than ${configured_limit}",
2663  ("configured_limit", configured_limit) );
2664 
2665  vector<htlc_object> result;
2666 
2667  const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_to_id >();
2668  auto htlc_index_end = htlc_idx.end();
2669  const account_id_type account = get_account_from_string(account_id_or_name)->id;
2670  auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));
2671 
2672  while(htlc_itr != htlc_index_end && htlc_itr->transfer.to == account && result.size() < limit)
2673  {
2674  result.push_back(*htlc_itr);
2675  ++htlc_itr;
2676  }
2677  return result;
2678 }
2679 
2680 vector<htlc_object> database_api::list_htlcs(const htlc_id_type start, uint32_t limit)const
2681 {
2682  return my->list_htlcs(start, limit);
2683 }
2684 
2685 vector<htlc_object> database_api_impl::list_htlcs(const htlc_id_type start, uint32_t limit) const
2686 {
2687  FC_ASSERT( _app_options, "Internal error" );
2688  const auto configured_limit = _app_options->api_limit_list_htlcs;
2689  FC_ASSERT( limit <= configured_limit,
2690  "limit can not be greater than ${configured_limit}",
2691  ("configured_limit", configured_limit) );
2692 
2693  vector<htlc_object> result;
2694  const auto& htlc_idx = _db.get_index_type<htlc_index>().indices().get<by_id>();
2695  auto itr = htlc_idx.lower_bound(start);
2696  while(itr != htlc_idx.end() && result.size() < limit)
2697  {
2698  result.push_back(*itr);
2699  ++itr;
2700  }
2701  return result;
2702 }
2703 
2705 // //
2706 // Private methods //
2707 // //
2709 
2710 const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id,
2711  bool throw_if_not_found ) const
2712 {
2713  // TODO cache the result to avoid repeatly fetching from db
2714  FC_ASSERT( name_or_id.size() > 0);
2715  const account_object* account = nullptr;
2716  if (std::isdigit(name_or_id[0]))
2717  account = _db.find(fc::variant(name_or_id, 1).as<account_id_type>(1));
2718  else
2719  {
2720  const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
2721  auto itr = idx.find(name_or_id);
2722  if (itr != idx.end())
2723  account = &*itr;
2724  }
2725  if(throw_if_not_found)
2726  FC_ASSERT( account, "no such account" );
2727  return account;
2728 }
2729 
2730 const asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id,
2731  bool throw_if_not_found ) const
2732 {
2733  // TODO cache the result to avoid repeatly fetching from db
2734  FC_ASSERT( symbol_or_id.size() > 0);
2735  const asset_object* asset = nullptr;
2736  if (std::isdigit(symbol_or_id[0]))
2737  asset = _db.find(fc::variant(symbol_or_id, 1).as<asset_id_type>(1));
2738  else
2739  {
2740  const auto& idx = _db.get_index_type<asset_index>().indices().get<by_symbol>();
2741  auto itr = idx.find(symbol_or_id);
2742  if (itr != idx.end())
2743  asset = &*itr;
2744  }
2745  if(throw_if_not_found)
2746  FC_ASSERT( asset, "no such asset" );
2747  return asset;
2748 }
2749 
2750 // helper function
2751 vector<optional<extended_asset_object>> database_api_impl::get_assets( const vector<asset_id_type>& asset_ids,
2752  optional<bool> subscribe )const
2753 {
2754  bool to_subscribe = get_whether_to_subscribe( subscribe );
2755  vector<optional<extended_asset_object>> result; result.reserve(asset_ids.size());
2756  std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result),
2757  [this,to_subscribe](asset_id_type id) -> optional<extended_asset_object> {
2758  if(auto o = _db.find(id))
2759  {
2760  if( to_subscribe )
2761  subscribe_to_item( id );
2762  return extend_asset( *o );
2763  }
2764  return {};
2765  });
2766  return result;
2767 }
2768 
2769 // helper function
2770 vector<limit_order_object> database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b,
2771  const uint32_t limit )const
2772 {
2773  FC_ASSERT( _app_options, "Internal error" );
2774  const auto configured_limit = _app_options->api_limit_get_limit_orders;
2775  FC_ASSERT( limit <= configured_limit,
2776  "limit can not be greater than ${configured_limit}",
2777  ("configured_limit", configured_limit) );
2778 
2779  const auto& limit_order_idx = _db.get_index_type<limit_order_index>();
2780  const auto& limit_price_idx = limit_order_idx.indices().get<by_price>();
2781 
2782  vector<limit_order_object> result;
2783  result.reserve(limit*2);
2784 
2785  uint32_t count = 0;
2786  auto limit_itr = limit_price_idx.lower_bound(price::max(a,b));
2787  auto limit_end = limit_price_idx.upper_bound(price::min(a,b));
2788  while(limit_itr != limit_end && count < limit)
2789  {
2790  result.push_back(*limit_itr);
2791  ++limit_itr;
2792  ++count;
2793  }
2794  count = 0;
2795  limit_itr = limit_price_idx.lower_bound(price::max(b,a));
2796  limit_end = limit_price_idx.upper_bound(price::min(b,a));
2797  while(limit_itr != limit_end && count < limit)
2798  {
2799  result.push_back(*limit_itr);
2800  ++limit_itr;
2801  ++count;
2802  }
2803 
2804  return result;
2805 }
2806 
2807 bool database_api_impl::is_impacted_account( const flat_set<account_id_type>& accounts)
2808 {
2809  if( !_subscribed_accounts.size() || !accounts.size() )
2810  return false;
2811 
2812  return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) {
2813  return _subscribed_accounts.find(account) != _subscribed_accounts.end();
2814  });
2815 }
2816 
2817 void database_api_impl::broadcast_updates( const vector<variant>& updates )
2818 {
2819  if( updates.size() && _subscribe_callback ) {
2820  auto capture_this = shared_from_this();
2821  fc::async([capture_this,updates](){
2822  if(capture_this->_subscribe_callback)
2823  capture_this->_subscribe_callback( fc::variant(updates) );
2824  });
2825  }
2826 }
2827 
2829 {
2830  if( queue.size() )
2831  {
2832  auto capture_this = shared_from_this();
2833  fc::async([capture_this, this, queue](){
2834  for( const auto& item : queue )
2835  {
2836  auto sub = _market_subscriptions.find(item.first);
2837  if( sub != _market_subscriptions.end() )
2838  sub->second( fc::variant(item.second ) );
2839  }
2840  });
2841  }
2842 }
2843 
2844 void database_api_impl::on_objects_removed( const vector<object_id_type>& ids,
2845  const vector<const object*>& objs,
2846  const flat_set<account_id_type>& impacted_accounts )
2847 {
2848  handle_object_changed(_notify_remove_create, false, ids, impacted_accounts,
2849  [objs](object_id_type id) -> const object* {
2850  auto it = std::find_if(
2851  objs.begin(), objs.end(),
2852  [id](const object* o) {return o != nullptr && o->id == id;});
2853 
2854  if (it != objs.end())
2855  return *it;
2856 
2857  return nullptr;
2858  }
2859  );
2860 }
2861 
2862 void database_api_impl::on_objects_new( const vector<object_id_type>& ids,
2863  const flat_set<account_id_type>& impacted_accounts )
2864 {
2865  handle_object_changed(_notify_remove_create, true, ids, impacted_accounts,
2866  std::bind(&object_database::find_object, &_db, std::placeholders::_1)
2867  );
2868 }
2869 
2870 void database_api_impl::on_objects_changed( const vector<object_id_type>& ids,
2871  const flat_set<account_id_type>& impacted_accounts )
2872 {
2873  handle_object_changed(false, true, ids, impacted_accounts,
2874  std::bind(&object_database::find_object, &_db, std::placeholders::_1)
2875  );
2876 }
2877 
2879  bool full_object,
2880  const vector<object_id_type>& ids,
2881  const flat_set<account_id_type>& impacted_accounts,
2882  std::function<const object*(object_id_type id)> find_object )
2883 {
2884  if( _subscribe_callback )
2885  {
2886  vector<variant> updates;
2887 
2888  for(auto id : ids)
2889  {
2890  if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )
2891  {
2892  if( full_object )
2893  {
2894  auto obj = find_object(id);
2895  if( obj )
2896  {
2897  updates.emplace_back( obj->to_variant() );
2898  }
2899  }
2900  else
2901  {
2902  updates.emplace_back( fc::variant( id, 1 ) );
2903  }
2904  }
2905  }
2906 
2907  if( updates.size() )
2908  broadcast_updates(updates);
2909  }
2910 
2911  if( _market_subscriptions.size() )
2912  {
2913  market_queue_type broadcast_queue;
2914 
2915  for(auto id : ids)
2916  {
2917  if( id.is<call_order_object>() )
2918  {
2919  enqueue_if_subscribed_to_market<call_order_object>( find_object(id), broadcast_queue, full_object );
2920  }
2921  else if( id.is<limit_order_object>() )
2922  {
2923  enqueue_if_subscribed_to_market<limit_order_object>( find_object(id), broadcast_queue, full_object );
2924  }
2925  else if( id.is<force_settlement_object>() )
2926  {
2927  enqueue_if_subscribed_to_market<force_settlement_object>( find_object(id), broadcast_queue,
2928  full_object );
2929  }
2930  }
2931 
2932  if( broadcast_queue.size() )
2933  broadcast_market_updates(broadcast_queue);
2934  }
2935 }
2936 
2941 {
2943  {
2944  auto capture_this = shared_from_this();
2945  block_id_type block_id = _db.head_block_id();
2946  fc::async([this,capture_this,block_id](){
2947  _block_applied_callback(fc::variant(block_id, 1));
2948  });
2949  }
2950 
2951  if(_market_subscriptions.size() == 0)
2952  return;
2953 
2954  const auto& ops = _db.get_applied_operations();
2955  map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;
2956  for(const optional< operation_history_object >& o_op : ops)
2957  {
2958  if( !o_op.valid() )
2959  continue;
2960  const operation_history_object& op = *o_op;
2961 
2962  optional< std::pair<asset_id_type,asset_id_type> > market;
2963  switch(op.op.which())
2964  {
2965  /* This is sent via the object_changed callback
2966  case operation::tag<limit_order_create_operation>::value:
2967  market = op.op.get<limit_order_create_operation>().get_market();
2968  break;
2969  */
2970  case operation::tag<fill_order_operation>::value:
2971  market = op.op.get<fill_order_operation>().get_market();
2972  break;
2973  /*
2974  case operation::tag<limit_order_cancel_operation>::value:
2975  */
2976  default: break;
2977  }
2978  if( market.valid() && _market_subscriptions.count(*market) )
2979  // FIXME this may cause fill_order_operation be pushed before order creation
2980  subscribed_markets_ops[*market].emplace_back(std::make_pair(op.op, op.result));
2981  }
2983  auto capture_this = shared_from_this();
2984  fc::async([this,capture_this,subscribed_markets_ops](){
2985  for(auto item : subscribed_markets_ops)
2986  {
2987  auto itr = _market_subscriptions.find(item.first);
2988  if(itr != _market_subscriptions.end())
2989  itr->second(fc::variant(item.second, GRAPHENE_NET_MAX_NESTED_OBJECTS));
2990  }
2991  });
2992 }
2993 
2994 } } // graphene::app
vector< force_settlement_object > get_settle_orders_by_account(const std::string &account_name_or_id, force_settlement_id_type start, uint32_t limit) const
Get forced settlement orders of a given account.
vector< balance_object > get_balance_objects(const vector< address > &addrs) const
Return all unclaimed balance objects for a list of addresses.
bool is_type() const
std::string price_to_string(const graphene::protocol::price &_price, const uint8_t base_precision, const uint8_t quote_precision)
Definition: util.cpp:68
vector< optional< extended_asset_object > > lookup_asset_symbols(const vector< string > &symbols_or_ids) const
Get a list of assets by symbol names or IDs.
vector< optional< account_object > > get_accounts(const vector< std::string > &account_names_or_ids, optional< bool > subscribe=optional< bool >()) const
Get a list of accounts by names or IDs.
tracks all of the proposal objects that requrie approval of an individual account.
std::string get_transaction_hex_without_sig(const signed_transaction &trx) const
#define FC_CAPTURE_AND_THROW(EXCEPTION_TYPE,...)
Definition: exception.hpp:357
vector< withdraw_permission_object > get_withdraw_permissions_by_giver(const std::string account_name_or_id, withdraw_permission_id_type start, uint32_t limit) const
Get non expired withdraw permission objects for a giver(ex:recurring customer)
set< address > get_potential_address_signatures(const signed_transaction &trx) const
vector< proposal_object > proposals
Definition: api_objects.hpp:72
map< pair< asset_id_type, asset_id_type >, std::function< void(const variant &)> > _market_subscriptions
auto async(Functor &&f, const char *desc FC_TASK_NAME_DEFAULT_ARG, priority prio=priority()) -> fc::future< decltype(f())>
Definition: thread.hpp:227
optional< signed_block > get_block(uint32_t block_num) const
void on_objects_changed(const vector< object_id_type > &ids, const flat_set< account_id_type > &impacted_accounts)
vector< liquidity_pool_object > get_liquidity_pools_by_both_assets(std::string asset_symbol_or_id_a, std::string asset_symbol_or_id_b, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
uint64_t api_limit_get_withdraw_permissions_by_recipient
Definition: application.hpp:74
void set_subscribe_callback(std::function< void(const variant &)> cb, bool notify_remove_create)
vector< optional< committee_member_object > > get_committee_members(const vector< committee_member_id_type > &committee_member_ids) const
map< string, account_id_type > lookup_accounts(const string &lower_bound_name, uint32_t limit, optional< bool > subscribe=optional< bool >()) const
Get names and IDs for registered accounts.
market_ticker get_ticker(const string &base, const string &quote, bool skip_order_book=false) const
processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const
vector< worker_object > get_workers_by_account(const std::string account_id_or_name) const
Wraps a derived index to intercept calls to create, modify, and remove so that callbacks may be fired...
Definition: index.hpp:309
fc::signal< void(const vector< object_id_type > &, const vector< const object * > &, const flat_set< account_id_type > &)> removed_objects
Definition: database.hpp:220
map< public_key_type, set< account_id_type >, pubkey_comparator > account_to_key_memberships
void set_block_applied_callback(std::function< void(const variant &block_id)> cb)
const T * find(object_id_type id) const
set< public_key_type > get_potential_signatures(const signed_transaction &trx) const
T as(uint32_t max_depth) const
Definition: variant.hpp:336
uint64_t get_asset_count() const
Get assets count.
set< public_key_type > get_potential_signatures(const signed_transaction &trx) const
vector< market_trade > get_trade_history_by_sequence(const string &base, const string &quote, int64_t start, fc::time_point_sec stop, unsigned limit=100) const
Returns trades for the market base:quote, ordered by time, most recent first. Note: Currently...
optional< block_header > get_block_header(uint32_t block_num) const
vector< vesting_balance_object > vesting_balances
Definition: api_objects.hpp:68
vector< call_order_object > call_orders
Definition: api_objects.hpp:70
const asset_object * get_asset_from_string(const std::string &symbol_or_id, bool throw_if_not_found=true) const
void pack(Stream &s, const flat_set< T, A... > &value, uint32_t _max_depth)
Definition: flat.hpp:11
vector< withdraw_permission_object > withdraws_to
Definition: api_objects.hpp:75
void cancel_all_subscriptions()
Stop receiving any notifications.
bool verify_account_authority(const string &account_name_or_id, const flat_set< public_key_type > &signers) const
map< custom_authority_id_type, rejected_predicate > rejected_predicate_map
Definition: transaction.hpp:31
contains properties that only apply to bitassets (market issued assets)
necessary to support nested operations inside the proposal_create_operation
Definition: operations.hpp:139
vector< blinded_balance_object > get_blinded_balances(const flat_set< commitment_type > &commitments) const
return the set of blinded balance objects by commitment ID
std::function< void(const fc::variant &)> _pending_trx_callback
bool is_public_key_registered(string public_key) const
vector< optional< witness_object > > get_witnesses(const vector< witness_id_type > &witness_ids) const
Get a list of witnesses by ID.
boost::signals2::scoped_connection _change_connection
void set_auto_subscription(bool enable)
set< address > get_potential_address_signatures(const signed_transaction &trx) const
const object * find_object(object_id_type id) const
vector< account_balance_object > balances
Definition: api_objects.hpp:67
fc::signal< void(const signed_block &)> applied_block
Definition: database.hpp:197
#define GRAPHENE_MAX_NESTED_OBJECTS
Definition: config.hpp:31
void subscribe_to_market(std::function< void(const variant &)> callback, const std::string &a, const std::string &b)
uint32_t sec_since_epoch() const
Definition: time.hpp:90
dynamic_global_property_object get_dynamic_global_properties() const
tracks the history of all logical operations on blockchain stateAll operations and virtual operations...
vector< withdraw_permission_object > get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit) const
void unsubscribe_from_market(const std::string &a, const std::string &b)
processed_transaction validate_transaction(const signed_transaction &trx) const
Validates a transaction against the current state without broadcasting it on the network.
This class represents an account on the object graphAccounts are the primary unit of authority on the...
bool is_impacted_account(const flat_set< account_id_type > &accounts)
vector< call_order_object > get_call_orders_by_account(const std::string &account_name_or_id, asset_id_type start, uint32_t limit) const
Get call orders (aka margin positions) of a given account.
Maintains global state information (committee_member list, current fees)This is an implementation det...
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
#define GRAPHENE_NET_MAX_NESTED_OBJECTS
Definition: config.hpp:110
vector< asset > get_vested_balances(const vector< balance_id_type > &objs) const
Calculate how much assets in the given balance objects are able to be claimed at current head block t...
vector< worker_object > get_workers_by_account(const std::string account_name_or_id) const
Get the workers owned by a given account.
An order-perserving dictionary of variant&#39;s.
void set_auto_subscription(bool enable)
Set auto-subscription behavior of follow-up API queries.
chain_id_type get_chain_id() const
Get the chain ID.
Definition: api.cpp:56
void on_objects_removed(const vector< object_id_type > &ids, const vector< const object * > &objs, const flat_set< account_id_type > &impacted_accounts)
vector< market_trade > get_trade_history(const string &base, const string &quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit=100) const
Returns recent trades for the market base:quote, ordered by time, most recent first. Note: Currently, timezone offsets are not supported. The time must be UTC. The range is [stop, start). In case when there are more than 100 trades occurred in the same second, this API only returns the first 100 records, can use another API get_trade_history_by_sequence to query for the rest.
optional< block_header > get_block_header(uint32_t block_num) const
Retrieve a block header.
vector< balance_object > get_balance_objects(const vector< address > &addrs) const
fc::signal< void(const vector< object_id_type > &, const flat_set< account_id_type > &)> changed_objects
Definition: database.hpp:215
market_volume get_24_volume(const string &base, const string &quote) const
Returns the 24 hour volume for the market assetA:assetB.
vector< market_ticker > get_top_markets(uint32_t limit) const
Returns vector of tickers sorted by reverse base_volume Note: this API is experimental and subject to...
vector< htlc_object > htlcs_to
Definition: api_objects.hpp:77
asset get_balance(account_id_type owner, asset_id_type asset_id) const
Retrieve a particular account&#39;s balance in a given asset.
Definition: db_balance.cpp:35
void set_block_applied_callback(std::function< void(const variant &block_id)> cb)
Register a callback handle which will get notified when a block is pushed to database.
This secondary index will allow fast access to the balance objects that belonging to an account...
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
database_api_impl(graphene::chain::database &db, const application_options *app_options)
map< uint32_t, optional< block_header > > get_block_header_batch(const vector< uint32_t > block_nums) const
Retrieve multiple block header by block numbers.
fc::time_point_sec date
const dynamic_global_property_object & get_dynamic_global_properties() const
Definition: db_getter.cpp:54
fc::static_variant< transfer_operation, limit_order_create_operation, limit_order_cancel_operation, call_order_update_operation, fill_order_operation, account_create_operation, account_update_operation, account_whitelist_operation, account_upgrade_operation, account_transfer_operation, asset_create_operation, asset_update_operation, asset_update_bitasset_operation, asset_update_feed_producers_operation, asset_issue_operation, asset_reserve_operation, asset_fund_fee_pool_operation, asset_settle_operation, asset_global_settle_operation, asset_publish_feed_operation, witness_create_operation, witness_update_operation, proposal_create_operation, proposal_update_operation, proposal_delete_operation, withdraw_permission_create_operation, withdraw_permission_update_operation, withdraw_permission_claim_operation, withdraw_permission_delete_operation, committee_member_create_operation, committee_member_update_operation, committee_member_update_global_parameters_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, worker_create_operation, custom_operation, assert_operation, balance_claim_operation, override_transfer_operation, transfer_to_blind_operation, blind_transfer_operation, transfer_from_blind_operation, asset_settle_cancel_operation, asset_claim_fees_operation, fba_distribute_operation, bid_collateral_operation, execute_bid_operation, asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, htlc_redeemed_operation, htlc_extend_operation, htlc_refund_operation, custom_authority_create_operation, custom_authority_update_operation, custom_authority_delete_operation, ticket_create_operation, ticket_update_operation, liquidity_pool_create_operation, liquidity_pool_delete_operation, liquidity_pool_deposit_operation, liquidity_pool_withdraw_operation, liquidity_pool_exchange_operation >
std::vector< variant > variants
Definition: variant.hpp:170
void set_pending_transaction_callback(std::function< void(const variant &)> cb)
void broadcast_updates(const vector< variant > &updates)
std::function< void(const fc::variant &)> _block_applied_callback
vector< htlc_object > list_htlcs(const htlc_id_type lower_bound_id, uint32_t limit) const
account_id_type side2_account_id
asset_id_type get_asset_id_from_string(const std::string &symbol_or_id) const
Get asset ID from an asset symbol or ID.
#define FC_THROW(...)
Definition: exception.hpp:366
boost::signals2::scoped_connection _pending_trx_connection
fc::static_variant< transfer_operation, limit_order_create_operation, limit_order_cancel_operation, call_order_update_operation, fill_order_operation, account_create_operation, account_update_operation, account_whitelist_operation, account_upgrade_operation, account_transfer_operation, asset_create_operation, asset_update_operation, asset_update_bitasset_operation, asset_update_feed_producers_operation, asset_issue_operation, asset_reserve_operation, asset_fund_fee_pool_operation, asset_settle_operation, asset_global_settle_operation, asset_publish_feed_operation, witness_create_operation, witness_update_operation, proposal_create_operation, proposal_update_operation, proposal_delete_operation, withdraw_permission_create_operation, withdraw_permission_update_operation, withdraw_permission_claim_operation, withdraw_permission_delete_operation, committee_member_create_operation, committee_member_update_operation, committee_member_update_global_parameters_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, worker_create_operation, custom_operation, assert_operation, balance_claim_operation, override_transfer_operation, transfer_to_blind_operation, blind_transfer_operation, transfer_from_blind_operation, asset_settle_cancel_operation, asset_claim_fees_operation, fba_distribute_operation, bid_collateral_operation, execute_bid_operation, asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, htlc_redeemed_operation, htlc_extend_operation, htlc_refund_operation, custom_authority_create_operation, custom_authority_update_operation, custom_authority_delete_operation, ticket_create_operation, ticket_update_operation, liquidity_pool_create_operation, liquidity_pool_delete_operation, liquidity_pool_deposit_operation, liquidity_pool_withdraw_operation, liquidity_pool_exchange_operation > operation
Definition: operations.hpp:118
vector< account_id_type > get_account_references(const std::string account_name_or_id) const
Get all accounts that refer to the specified account in their owner or active authorities.
optional< vesting_balance_object > cashback_balance
Definition: api_objects.hpp:66
bool get_whether_to_subscribe(optional< bool > subscribe) const
vector< extended_asset_object > get_assets_by_issuer(const std::string &issuer_name_or_id, asset_id_type start, uint32_t limit) const
vector< force_settlement_object > settle_orders
Definition: api_objects.hpp:71
#define GRAPHENE_DB_MAX_INSTANCE_ID
Definition: object_id.hpp:28
vector< limit_order_object > get_limit_orders(const std::string &a, const std::string &b, uint32_t limit) const
const vesting_balance_object & cashback_balance(const DB &db) const
bool verify_authority(const signed_transaction &trx) const
optional< liquidity_pool_id_type > for_liquidity_pool
The ID of the liquidity pool if the asset is the share asset of a liquidity pool. ...
account_id_type side1_account_id
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
vector< limit_order_object > get_account_limit_orders(const string &account_name_or_id, const string &base, const string &quote, uint32_t limit=101, optional< limit_order_id_type > ostart_id=optional< limit_order_id_type >(), optional< price > ostart_price=optional< price >())
Fetch all orders relevant to the specified account and specified market, result orders are sorted des...
market_volume get_24_volume(const string &base, const string &quote) const
tag_type which() const
const T & get(object_id_type id) const
price min() const
Definition: asset.hpp:131
optional< signed_block > fetch_block_by_number(uint32_t num) const
Definition: db_block.cpp:75
fc::optional< witness_object > get_witness_by_account(const std::string account_name_or_id) const
Get the witness owned by a given account.
std::string get_transaction_hex_without_sig(const signed_transaction &trx) const
Get a hexdump of the serialized binary form of a signatures-stripped transaction. ...
The proposal_create_operation creates a transaction proposal, for use in multi-sig scenariosCreates a...
Definition: proposal.hpp:70
vector< optional< witness_object > > get_witnesses(const vector< witness_id_type > &witness_ids) const
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
vector< extended_asset_object > get_assets_by_issuer(const std::string &issuer_name_or_id, asset_id_type start, uint32_t limit) const
Get assets issued (owned) by a given account.
vector< liquidity_pool_object > get_liquidity_pools_by_asset_b(std::string asset_symbol_or_id, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
vector< authority > get_viable_custom_authorities(account_id_type account, const operation &op, rejected_predicate_map *rejected_authorities=nullptr) const
Get a list of custom authorities which can validate the provided operation for the provided account...
Definition: db_getter.cpp:99
vector< call_order_object > get_call_orders(const std::string &a, uint32_t limit) const
fc::optional< committee_member_object > get_committee_member_by_account(const std::string account_id_or_name) const
vector< htlc_object > get_htlc_by_to(const std::string account_name_or_id, htlc_id_type start, uint32_t limit) const
Get non expired HTLC objects using the receiver account.
fc::signal< void(const signed_transaction &)> on_pending_transaction
Definition: database.hpp:203
vector< htlc_object > get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const
std::set< account_id_type > _subscribed_accounts
vector< account_statistics_object > get_top_voters(uint32_t limit) const
Returns vector of voting power sorted by reverse vp_active.
vector< worker_object > get_all_workers(const optional< bool > is_expired=optional< bool >()) const
get_required_fees_helper(const fee_schedule &_current_fee_schedule, const price &_core_exchange_rate, uint32_t _max_recursion)
vector< extended_asset_object > list_assets(const string &lower_bound_symbol, uint32_t limit) const
Get assets alphabetically by symbol name.
uint64_t get_account_count() const
Get the total number of accounts registered with the blockchain.
vector< fc::variant > get_required_fees(const vector< operation > &ops, const std::string &asset_symbol_or_id) const
For each operation calculate the required fee in the specified asset type.
virtual const object * find(object_id_type id) const override
Definition: index.hpp:332
const vector< optional< operation_history_object > > & get_applied_operations() const
Definition: db_block.cpp:548
chain_property_object get_chain_properties() const
vector< fc::variant > get_required_fees(const vector< operation > &ops, const std::string &asset_id_or_symbol) const
vector< market_trade > get_trade_history(const string &base, const string &quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit=100) const
Transfers an amount of one asset from one account to another.
Definition: transfer.hpp:45
object_id_type id
Definition: object.hpp:69
vector< worker_object > get_all_workers(const optional< bool > is_expired=optional< bool >()) const
Get workers.
void to_variant(const flat_set< T, A... > &var, variant &vo, uint32_t _max_depth)
Definition: flat.hpp:105
vector< variant > votes
Definition: api_objects.hpp:65
#define GET_REQUIRED_FEES_MAX_RECURSION
vector< asset > get_named_account_balances(const std::string &name, const flat_set< asset_id_type > &assets) const
Semantically equivalent to get_account_balances.
vector< limit_order_object > get_account_limit_orders(const string &account_name_or_id, const string &base, const string &quote, uint32_t limit, optional< limit_order_id_type > ostart_id, optional< price > ostart_price)
uint64_t get_worker_count() const
Get the total number of workers registered with the blockchain.
flat_set< vote_id_type > votes
Definition: account.hpp:58
account_id_type get_id() const
void handle_object_changed(bool force_notify, bool full_object, const vector< object_id_type > &ids, const flat_set< account_id_type > &impacted_accounts, std::function< const object *(object_id_type id)> find_object)
contains only the public point of an elliptic curve key.
Definition: elliptic.hpp:35
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:114
optional< signed_block > get_block(uint32_t block_num) const
Retrieve a full, signed block.
optional< account_object > get_account_by_name(string name) const
Get info of an account by name.
fc::variants get_objects(const vector< object_id_type > &ids, optional< bool > subscribe) const
void verify_authority(const vector< operation > &ops, const flat_set< public_key_type > &sigs, const std::function< const authority *(account_id_type)> &get_active, const std::function< const authority *(account_id_type)> &get_owner, const custom_authority_lookup &get_custom, bool allow_non_immediate_owner, bool ignore_custom_operation_required_auths, uint32_t max_recursion=GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committee=false, const flat_set< account_id_type > &active_aprovals=flat_set< account_id_type >(), const flat_set< account_id_type > &owner_approvals=flat_set< account_id_type >())
vector< asset_id_type > assets
Definition: api_objects.hpp:73
optional< vesting_balance_id_type > cashback_vb
std::map< string, full_account > get_full_accounts(const vector< string > &names_or_ids, optional< bool > subscribe)
std::map< string, full_account > get_full_accounts(const vector< string > &names_or_ids, optional< bool > subscribe=optional< bool >())
Fetch all objects relevant to the specified accounts and optionally subscribe to updates.
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
optional< account_object > get_account_by_name(string name) const
account_id_type lifetime_referrer
The lifetime member at the top of the referral tree. Receives a percentage of referral rewards...
vector< limit_order_object > get_limit_orders(std::string a, std::string b, uint32_t limit) const
Get limit orders in a given market.
account_id_type get_account_id_from_string(const std::string &name_or_id) const
Get account object from a name or ID.
processed_transaction validate_transaction(const signed_transaction &trx)
Definition: db_block.cpp:304
std::function< void(const fc::variant &)> _subscribe_callback
string symbol
Ticker symbol for this asset, i.e. "USD".
This secondary index will allow a reverse lookup of all accounts that a particular key or account is ...
vector< flat_set< account_id_type > > get_key_references(vector< public_key_type > keys) const
Get all accounts that refer to the specified public keys in their owner authority, active authorities or memo key.
vector< asset > get_account_balances(const std::string &account_name_or_id, const flat_set< asset_id_type > &assets) const
vector< liquidity_pool_object > get_liquidity_pools_by_both_assets(std::string asset_symbol_or_id_a, std::string asset_symbol_or_id_b, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
Get a list of liquidity pools by the symbols or IDs of the two assets in the pool.
optional< htlc_object > get_htlc(htlc_id_type id, optional< bool > subscribe=optional< bool >()) const
Get HTLC object.
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
fc::variant set_op_fees(operation &op)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
graphene::chain::database & _db
void subscribe_to_market(std::function< void(const variant &)> callback, const std::string &a, const std::string &b)
Request notification when the active orders in the market between two assets changes.
vector< optional< extended_asset_object > > lookup_asset_symbols(const vector< string > &symbols_or_ids) const
map< string, witness_id_type > lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const
Get names and IDs for registered witnesses.
vector< call_order_object > get_margin_positions(const std::string account_name_or_id) const
Get all open margin positions of a given account.
map< string, committee_member_id_type > lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const
Get names and IDs for registered committee_members.
vector< proposal_object > get_proposed_transactions(const std::string account_name_or_id) const
return a set of proposed transactions (aka proposals) that the specified account can add approval to ...
vector< vesting_balance_object > get_vesting_balances(const std::string account_name_or_id) const
Return all vesting balance objects owned by an account.
order_book get_order_book(const string &base, const string &quote, unsigned limit=50) const
vector< withdraw_permission_object > get_withdraw_permissions_by_recipient(const std::string account_name_or_id, withdraw_permission_id_type start, uint32_t limit) const
Get non expired withdraw permission objects for a recipient(ex:service provider)
chain_id_type get_chain_id() const
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object&#39;s.
Definition: variant.hpp:198
const fee_schedule & current_fee_schedule() const
Definition: db_getter.cpp:59
processed_transaction validate_transaction(const signed_transaction &trx) const
std::string get_transaction_hex(const signed_transaction &trx) const
Get a hexdump of the serialized binary form of a transaction.
vector< collateral_bid_object > get_collateral_bids(const std::string &a, uint32_t limit, uint32_t start) const
Get collateral_bid_objects for a given asset.
market_ticker get_ticker(const string &base, const string &quote) const
Returns the ticker for the market assetA:assetB.
extended_asset_object extend_asset(ASSET &&a) const
const T & get_secondary_index() const
Definition: index.hpp:177
optional< htlc_object > get_htlc(htlc_id_type id, optional< bool > subscribe) const
vector< call_order_object > get_margin_positions(const std::string account_id_or_name) const
vector< account_id_type > get_account_references(const std::string account_id_or_name) const
fc::variant_object get_config()
Definition: get_config.cpp:31
vector< flat_set< account_id_type > > get_key_references(vector< public_key_type > key) const
vector< account_statistics_object > get_top_voters(uint32_t limit) const
account_statistics_object statistics
Definition: api_objects.hpp:61
dynamic_global_property_object get_dynamic_global_properties() const
Retrieve the current graphene::chain::dynamic_global_property_object.
void subscribe_to_item(const T &item) const
void set_pending_transaction_callback(std::function< void(const variant &signed_transaction_object)> cb)
Register a callback handle which will get notified when a transaction is pushed to database...
vector< force_settlement_object > get_settle_orders_by_account(const std::string &account_name_or_id, force_settlement_id_type start, uint32_t limit) const
vector< asset > get_vested_balances(const vector< balance_id_type > &objs) const
bool is_liquidity_pool_share_asset() const
std::string get_transaction_hex(const signed_transaction &trx) const
uint64_t get_committee_count() const
Get the total number of committee registered with the blockchain.
boost::signals2::scoped_connection _new_connection
vector< limit_order_object > get_limit_orders_by_account(const string &account_name_or_id, optional< uint32_t > limit, optional< limit_order_id_type > start_id)
boost::signals2::scoped_connection _applied_block_connection
std::map< std::pair< graphene::chain::asset_id_type, graphene::chain::asset_id_type >, std::vector< fc::variant > > market_queue_type
vector< htlc_object > get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const
fc::variant_object get_config() const
map< uint32_t, optional< block_header > > get_block_header_batch(const vector< uint32_t > block_nums) const
vector< proposal_object > get_proposed_transactions(const std::string account_id_or_name) const
void on_objects_new(const vector< object_id_type > &ids, const flat_set< account_id_type > &impacted_accounts)
tracks the parameters of an assetAll assets have a globally unique symbol name that controls how they...
bool verify_account_authority(const string &account_name_or_id, const flat_set< public_key_type > &signers) const
Verify that the public keys have enough authority to approve an operation for this account...
vector< liquidity_pool_object > get_liquidity_pools_by_asset_a(std::string asset_symbol_or_id, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
vector< limit_order_object > limit_orders
Definition: api_objects.hpp:69
typename impl::transform< List, Transformer >::type transform
Transform elements of a typelist.
Definition: typelist.hpp:170
const account_object * get_account_from_string(const std::string &name_or_id, bool throw_if_not_found=true) const
#define dlog(FORMAT,...)
Definition: logger.hpp:100
void broadcast_market_updates(const market_queue_type &queue)
safe< int64_t > share_type
Definition: types.hpp:247
vector< force_settlement_object > get_settle_orders(const std::string &a, uint32_t limit) const
map< string, committee_member_id_type > lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const
vector< optional< liquidity_pool_object > > get_liquidity_pools_by_share_asset(const vector< std::string > &asset_symbols_or_ids, optional< bool > subscribe=optional< bool >()) const
const object & get(object_id_type id) const
Definition: index.hpp:111
virtual const object * find(object_id_type id) const override
asset_id_type asset_id
Definition: asset.hpp:39
global_property_object get_global_properties() const
Retrieve the current graphene::chain::global_property_object.
bool verify_authority(const signed_transaction &trx) const
map< string, account_id_type > lookup_accounts(const string &lower_bound_name, uint32_t limit, optional< bool > subscribe) const
account_id_type from
Account to transfer asset from.
Definition: transfer.hpp:54
bitasset_options options
The tunable options for BitAssets are stored in this field.
vector< call_order_object > get_call_orders(const std::string &a, uint32_t limit) const
Get call orders (aka margin positions) for a given asset.
vector< optional< extended_asset_object > > get_assets(const vector< std::string > &asset_symbols_or_ids, optional< bool > subscribe=optional< bool >()) const
Get a list of assets by symbol names or IDs.
set< public_key_type > get_required_signatures(const signed_transaction &trx, const flat_set< public_key_type > &available_keys) const
set< public_key_type > get_required_signatures(const signed_transaction &trx, const flat_set< public_key_type > &available_keys) const
vector< optional< account_object > > get_accounts(const vector< std::string > &account_names_or_ids, optional< bool > subscribe) const
fc::variant_object get_config() const
Retrieve compile-time constants.
account_statistics_id_type statistics
bool is_subscribed_to_item(const T &item) const
asset_id_type get_id() const
vector< force_settlement_object > get_settle_orders(const std::string &a, uint32_t limit) const
Get forced settlement orders in a given asset.
bool is_public_key_registered(string public_key) const
order_book get_order_book(const string &base, const string &quote, unsigned limit=50) const
Returns the order book for the market base:quote.
map< string, witness_id_type > lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const
Maintains global state information (committee_member list, current fees)This is an implementation det...
void unsubscribe_from_market(const std::string &a, const std::string &b)
Unsubscribe from updates to a given market.
fc::variant set_proposal_create_op_fees(operation &proposal_create_op)
vector< liquidity_pool_object > get_liquidity_pools_by_asset_b(std::string asset_symbol_or_id, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
Get a list of liquidity pools by the symbol or ID of the second asset in the pool.
account_id_type registrar
The account that paid the fee to register this account. Receives a percentage of referral rewards...
vector< htlc_object > list_htlcs(const htlc_id_type start, uint32_t limit) const
Get all HTLCs.
fc::optional< committee_member_object > get_committee_member_by_account(const string account_name_or_id) const
Get the committee_member owned by a given account.
vector< optional< account_object > > lookup_account_names(const vector< string > &account_names) const
Get a list of accounts by name.
processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const
used to fetch an individual transaction.
static time_point now()
Definition: time.cpp:13
const chain_id_type & get_chain_id() const
Definition: db_getter.cpp:84
vector< htlc_object > htlcs_from
Definition: api_objects.hpp:76
void cancel_all_subscriptions(bool reset_callback, bool reset_market_subscriptions)
vector< market_ticker > get_top_markets(uint32_t limit) const
vector< optional< account_object > > lookup_account_names(const vector< string > &account_names) const
vector< withdraw_permission_object > withdraws_from
Definition: api_objects.hpp:74
vector< collateral_bid_object > get_collateral_bids(const std::string &asset, uint32_t limit, uint32_t start) const
vector< call_order_object > get_call_orders_by_account(const std::string &account_name_or_id, asset_id_type start, uint32_t limit) const
global_property_object get_global_properties() const
fc::ripemd160 transaction_id_type
Definition: types.hpp:244
optional< signed_transaction > get_recent_transaction_by_id(const transaction_id_type &txid) const
block_id_type head_block_id() const
Definition: db_getter.cpp:74
chain_property_object get_chain_properties() const
Retrieve the graphene::chain::chain_property_object associated with the chain.
vector< vesting_balance_object > get_vesting_balances(const std::string account_id_or_name) const
vector< limit_order_object > get_limit_orders_by_account(const string &account_name_or_id, optional< uint32_t > limit=101, optional< limit_order_id_type > start_id=optional< limit_order_id_type >())
Fetch open limit orders in all markets relevant to the specified account, ordered by ID...
vector< variant > lookup_vote_ids(const vector< vote_id_type > &votes) const
Given a set of votes, return the objects they are voting for.
std::string to_hex(const char *d, uint32_t s)
Definition: hex.cpp:17
tracks information about a committee_member account.A committee_member is responsible for setting blo...
uint64_t get_witness_count() const
Get the total number of witnesses registered with the blockchain.
vector< market_trade > get_trade_history_by_sequence(const string &base, const string &quote, int64_t start, fc::time_point_sec stop, unsigned limit=100) const
fc::optional< witness_object > get_witness_by_account(const std::string account_id_or_name) const
void for_each(T &&t, const account_object &a, seq< Is... >)
Definition: database.hpp:701
vector< blinded_balance_object > get_blinded_balances(const flat_set< commitment_type > &commitments) const
const application_options * _app_options
captures the result of evaluating the operations contained in the transaction
void set_subscribe_callback(std::function< void(const variant &)> cb, bool notify_remove_create)
Register a callback handle which then can be used to subscribe to object database changes...
price max() const
Definition: asset.hpp:130
vector< optional< committee_member_object > > get_committee_members(const vector< committee_member_id_type > &committee_member_ids) const
Get a list of committee_members by ID.
account_id_type referrer
The account credited as referring this account. Receives a percentage of referral rewards...
const asset_bitasset_data_object & bitasset_data(const DB &db) const
vector< optional< liquidity_pool_object > > get_liquidity_pools_by_share_asset(const vector< std::string > &asset_symbols_or_ids, optional< bool > subscribe=optional< bool >()) const
Get a list of liquidity pools by their share asset symbols or IDs.
vector< asset > get_account_balances(const std::string &account_name_or_id, const flat_set< asset_id_type > &assets) const
Get an account&#39;s balances in various assets.
vector< liquidity_pool_object > get_liquidity_pools_by_asset_a(std::string asset_symbol_or_id, optional< uint32_t > limit=101, optional< liquidity_pool_id_type > start_id=optional< liquidity_pool_id_type >()) const
Get a list of liquidity pools by the symbol or ID of the first asset in the pool. ...
vector< optional< extended_asset_object > > get_assets(const vector< std::string > &asset_symbols_or_ids, optional< bool > subscribe) const
const graphene::api_helper_indexes::amount_in_collateral_index * amount_in_collateral_index
vector< variant > lookup_vote_ids(const vector< vote_id_type > &votes) const
const IndexType & get_index_type() const
fc::variants get_objects(const vector< object_id_type > &ids, optional< bool > subscribe=optional< bool >()) const
Get the objects corresponding to the provided IDs.
vector< htlc_object > get_htlc_by_from(const std::string account_name_or_id, htlc_id_type start, uint32_t limit) const
Get non expired HTLC objects using the sender account.
boost::signals2::scoped_connection _removed_connection
an offer to sell a amount of a asset at a specified exchange rate by a certain timeThis limit_order_o...
fc::signal< void(const vector< object_id_type > &, const flat_set< account_id_type > &)> new_objects
Definition: database.hpp:209
vector< withdraw_permission_object > get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit) const
const index_type & indices() const
vector< extended_asset_object > list_assets(const string &lower_bound_symbol, uint32_t limit) const