BitShares-Core  6.0.1
BitShares blockchain implementation and command-line interface software
application.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 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 #include <graphene/app/api.hpp>
27 #include <graphene/app/plugin.hpp>
28 
33 
35 
38 
41 
42 #include <fc/asio.hpp>
43 #include <fc/io/fstream.hpp>
45 #include <fc/rpc/websocket_api.hpp>
46 #include <fc/crypto/base64.hpp>
47 
48 #include <boost/filesystem/path.hpp>
49 #include <boost/signals2.hpp>
50 #include <boost/range/algorithm/reverse.hpp>
51 #include <boost/algorithm/string.hpp>
52 
53 #include <iostream>
54 
55 #include <fc/log/file_appender.hpp>
56 #include <fc/log/logger.hpp>
57 #include <fc/log/logger_config.hpp>
58 
59 #include <boost/range/adaptor/reversed.hpp>
60 
61 namespace graphene { namespace app {
62 using net::item_hash_t;
63 using net::item_id;
64 using net::message;
65 using net::block_message;
66 using net::trx_message;
67 
68 using chain::block_header;
69 using chain::signed_block_header;
70 using chain::signed_block;
72 
73 using std::vector;
74 
75 namespace bpo = boost::program_options;
76 
77 namespace detail {
78 
80  auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
81  dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
85  initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /
86  initial_state.initial_parameters.block_interval *
87  initial_state.initial_parameters.block_interval);
88  for( uint64_t i = 0; i < initial_state.initial_active_witnesses; ++i )
89  {
90  auto name = "init"+fc::to_string(i);
91  initial_state.initial_accounts.emplace_back(name,
92  nathan_key.get_public_key(),
93  nathan_key.get_public_key(),
94  true);
95  initial_state.initial_committee_candidates.push_back({name});
96  initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key()});
97  }
98 
99  initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());
100  initial_state.initial_balances.push_back({nathan_key.get_public_key(),
103  initial_state.initial_chain_id = fc::sha256::hash( "BOGUS" );
104 
105  return initial_state;
106  }
107 
108 
109 }
110 
111 }}
112 
113 #include "application_impl.hxx"
114 
115 namespace graphene { namespace app { namespace detail {
116 
118 {
119  this->shutdown();
120 }
121 
123 { try {
124  _p2p_network = std::make_shared<net::node>("BitShares Reference Implementation");
125 
126  _p2p_network->load_configuration(data_dir / "p2p");
127  _p2p_network->set_node_delegate(shared_from_this());
128 
129  if( _options->count("seed-node") > 0 )
130  {
131  auto seeds = _options->at("seed-node").as<vector<string>>();
132  _p2p_network->add_seed_nodes(seeds);
133  }
134 
135  if( _options->count("seed-nodes") > 0 )
136  {
137  auto seeds_str = _options->at("seed-nodes").as<string>();
138  auto seeds = fc::json::from_string(seeds_str).as<vector<string>>(2);
139  _p2p_network->add_seed_nodes(seeds);
140  }
141  else
142  {
143  // https://bitsharestalk.org/index.php/topic,23715.0.html
144  vector<string> seeds = {
145  #include "../egenesis/seed-nodes.txt"
146  };
147  _p2p_network->add_seed_nodes(seeds);
148  }
149 
150  if( _options->count("p2p-endpoint") > 0 )
151  _p2p_network->listen_on_endpoint(fc::ip::endpoint::from_string(_options->at("p2p-endpoint").as<string>()), true);
152  else
153  _p2p_network->listen_on_port(0, false);
154  _p2p_network->listen_to_p2p_network();
155  ilog("Configured p2p node to listen on ${ip}", ("ip", _p2p_network->get_actual_listening_endpoint()));
156 
157  _p2p_network->connect_to_p2p_network();
159  _chain_db->head_block_id()),
160  std::vector<uint32_t>());
162 
164 {
165  auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_NET_MAX_NESTED_OBJECTS);
166  auto login = std::make_shared<graphene::app::login_api>( _self );
167  login->enable_api("database_api");
168 
169  wsc->register_api(login->database());
170  wsc->register_api(fc::api<graphene::app::login_api>(login));
171  c->set_session_data( wsc );
172 
173  std::string username = "*";
174  std::string password = "*";
175 
176  // Try to extract login information from "Authorization" header if present
177  std::string auth = c->get_request_header("Authorization");
178  if( boost::starts_with(auth, "Basic ") ) {
179 
180  FC_ASSERT( auth.size() > 6 );
181  auto user_pass = fc::base64_decode(auth.substr(6));
182 
183  std::vector<std::string> parts;
184  boost::split( parts, user_pass, boost::is_any_of(":") );
185 
186  FC_ASSERT(parts.size() == 2);
187 
188  username = parts[0];
189  password = parts[1];
190  }
191 
192  login->login(username, password);
193 }
194 
196 { try {
197  if( 0 == _options->count("rpc-endpoint") )
198  return;
199 
200  string proxy_forward_header;
201  if( _options->count("proxy-forwarded-for-header") > 0 )
202  proxy_forward_header = _options->at("proxy-forwarded-for-header").as<string>();
203 
204  _websocket_server = std::make_shared<fc::http::websocket_server>( proxy_forward_header );
205  _websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
206 
207  ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
208  _websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
209  _websocket_server->start_accept();
211 
213 { try {
214  if( 0 == _options->count("rpc-tls-endpoint") )
215  return;
216  if( 0 == _options->count("server-pem") )
217  {
218  wlog( "Please specify a server-pem to use rpc-tls-endpoint" );
219  return;
220  }
221 
222  string proxy_forward_header;
223  if( _options->count("proxy-forwarded-for-header") > 0 )
224  proxy_forward_header = _options->at("proxy-forwarded-for-header").as<string>();
225 
226  string password = ( _options->count("server-pem-password") > 0 ) ?
227  _options->at("server-pem-password").as<string>() : "";
228  _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(
229  _options->at("server-pem").as<string>(), password, proxy_forward_header );
230  _websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
231 
232  ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
233  _websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
234  _websocket_tls_server->start_accept();
236 
237 void application_impl::initialize(const fc::path& data_dir, shared_ptr<boost::program_options::variables_map> options)
238 {
239  _data_dir = data_dir;
240  _options = options;
241 
242  if ( _options->count("io-threads") > 0 )
243  {
244  const uint16_t num_threads = _options->at("io-threads").as<uint16_t>();
246  }
247 
248  if( _options->count("force-validate") > 0 )
249  {
250  ilog( "All transaction signatures will be validated" );
251  _force_validate = true;
252  }
253 
254  if ( _options->count("enable-subscribe-to-all") > 0 )
255  _app_options.enable_subscribe_to_all = _options->at( "enable-subscribe-to-all" ).as<bool>();
256 
257  set_api_limit();
258 
259  if( is_plugin_enabled( "market_history" ) )
260  _app_options.has_market_history_plugin = true;
261  else
262  ilog("Market history plugin is not enabled");
263 
264  if( is_plugin_enabled( "api_helper_indexes" ) )
265  _app_options.has_api_helper_indexes_plugin = true;
266  else
267  ilog("API helper indexes plugin is not enabled");
268 
269  if( _options->count("api-access") > 0 )
270  {
271 
272  fc::path api_access_file = _options->at("api-access").as<boost::filesystem::path>();
273 
274  FC_ASSERT( fc::exists(api_access_file),
275  "Failed to load file from ${path}", ("path", api_access_file) );
276 
277  _apiaccess = fc::json::from_file( api_access_file ).as<api_access>( 20 );
278  ilog( "Using api access file from ${path}",
279  ("path", api_access_file) );
280  }
281  else
282  {
283  // TODO: Remove this generous default access policy
284  // when the UI logs in properly
285  _apiaccess = api_access();
286  api_access_info wild_access;
287  wild_access.password_hash_b64 = "*";
288  wild_access.password_salt_b64 = "*";
289  wild_access.allowed_apis.push_back( "database_api" );
290  wild_access.allowed_apis.push_back( "network_broadcast_api" );
291  wild_access.allowed_apis.push_back( "history_api" );
292  wild_access.allowed_apis.push_back( "orders_api" );
293  wild_access.allowed_apis.push_back( "custom_operations_api" );
294  _apiaccess.permission_map["*"] = wild_access;
295  }
296 
297  initialize_plugins();
298 }
299 
301  if (_options->count("api-limit-get-account-history-operations") > 0) {
302  _app_options.api_limit_get_account_history_operations =
303  _options->at("api-limit-get-account-history-operations").as<uint64_t>();
304  }
305  if(_options->count("api-limit-get-account-history") > 0){
306  _app_options.api_limit_get_account_history =
307  _options->at("api-limit-get-account-history").as<uint64_t>();
308  }
309  if(_options->count("api-limit-get-grouped-limit-orders") > 0){
310  _app_options.api_limit_get_grouped_limit_orders =
311  _options->at("api-limit-get-grouped-limit-orders").as<uint64_t>();
312  }
313  if(_options->count("api-limit-get-relative-account-history") > 0){
314  _app_options.api_limit_get_relative_account_history =
315  _options->at("api-limit-get-relative-account-history").as<uint64_t>();
316  }
317  if(_options->count("api-limit-get-account-history-by-operations") > 0){
318  _app_options.api_limit_get_account_history_by_operations =
319  _options->at("api-limit-get-account-history-by-operations").as<uint64_t>();
320  }
321  if(_options->count("api-limit-get-asset-holders") > 0){
322  _app_options.api_limit_get_asset_holders =
323  _options->at("api-limit-get-asset-holders").as<uint64_t>();
324  }
325  if(_options->count("api-limit-get-key-references") > 0){
326  _app_options.api_limit_get_key_references =
327  _options->at("api-limit-get-key-references").as<uint64_t>();
328  }
329  if(_options->count("api-limit-get-htlc-by") > 0) {
330  _app_options.api_limit_get_htlc_by =
331  _options->at("api-limit-get-htlc-by").as<uint64_t>();
332  }
333  if(_options->count("api-limit-get-full-accounts") > 0) {
334  _app_options.api_limit_get_full_accounts =
335  _options->at("api-limit-get-full-accounts").as<uint64_t>();
336  }
337  if(_options->count("api-limit-get-full-accounts-lists") > 0) {
338  _app_options.api_limit_get_full_accounts_lists =
339  _options->at("api-limit-get-full-accounts-lists").as<uint64_t>();
340  }
341  if(_options->count("api-limit-get-top-voters") > 0) {
342  _app_options.api_limit_get_top_voters =
343  _options->at("api-limit-get-top-voters").as<uint64_t>();
344  }
345  if(_options->count("api-limit-get-call-orders") > 0) {
346  _app_options.api_limit_get_call_orders =
347  _options->at("api-limit-get-call-orders").as<uint64_t>();
348  }
349  if(_options->count("api-limit-get-settle-orders") > 0) {
350  _app_options.api_limit_get_settle_orders =
351  _options->at("api-limit-get-settle-orders").as<uint64_t>();
352  }
353  if(_options->count("api-limit-get-assets") > 0) {
354  _app_options.api_limit_get_assets =
355  _options->at("api-limit-get-assets").as<uint64_t>();
356  }
357  if(_options->count("api-limit-get-limit-orders") > 0){
358  _app_options.api_limit_get_limit_orders =
359  _options->at("api-limit-get-limit-orders").as<uint64_t>();
360  }
361  if(_options->count("api-limit-get-limit-orders-by-account") > 0){
362  _app_options.api_limit_get_limit_orders_by_account =
363  _options->at("api-limit-get-limit-orders-by-account").as<uint64_t>();
364  }
365  if(_options->count("api-limit-get-order-book") > 0){
366  _app_options.api_limit_get_order_book =
367  _options->at("api-limit-get-order-book").as<uint64_t>();
368  }
369  if(_options->count("api-limit-list-htlcs") > 0){
370  _app_options.api_limit_list_htlcs =
371  _options->at("api-limit-list-htlcs").as<uint64_t>();
372  }
373  if(_options->count("api-limit-lookup-accounts") > 0) {
374  _app_options.api_limit_lookup_accounts =
375  _options->at("api-limit-lookup-accounts").as<uint64_t>();
376  }
377  if(_options->count("api-limit-lookup-witness-accounts") > 0) {
378  _app_options.api_limit_lookup_witness_accounts =
379  _options->at("api-limit-lookup-witness-accounts").as<uint64_t>();
380  }
381  if(_options->count("api-limit-lookup-committee-member-accounts") > 0) {
382  _app_options.api_limit_lookup_committee_member_accounts =
383  _options->at("api-limit-lookup-committee-member-accounts").as<uint64_t>();
384  }
385  if(_options->count("api-limit-lookup-vote-ids") > 0) {
386  _app_options.api_limit_lookup_vote_ids =
387  _options->at("api-limit-lookup-vote-ids").as<uint64_t>();
388  }
389  if(_options->count("api-limit-get-account-limit-orders") > 0) {
390  _app_options.api_limit_get_account_limit_orders =
391  _options->at("api-limit-get-account-limit-orders").as<uint64_t>();
392  }
393  if(_options->count("api-limit-get-collateral-bids") > 0) {
394  _app_options.api_limit_get_collateral_bids =
395  _options->at("api-limit-get-collateral-bids").as<uint64_t>();
396  }
397  if(_options->count("api-limit-get-top-markets") > 0) {
398  _app_options.api_limit_get_top_markets =
399  _options->at("api-limit-get-top-markets").as<uint64_t>();
400  }
401  if(_options->count("api-limit-get-trade-history") > 0) {
402  _app_options.api_limit_get_trade_history =
403  _options->at("api-limit-get-trade-history").as<uint64_t>();
404  }
405  if(_options->count("api-limit-get-trade-history-by-sequence") > 0) {
406  _app_options.api_limit_get_trade_history_by_sequence =
407  _options->at("api-limit-get-trade-history-by-sequence").as<uint64_t>();
408  }
409  if(_options->count("api-limit-get-withdraw-permissions-by-giver") > 0) {
410  _app_options.api_limit_get_withdraw_permissions_by_giver =
411  _options->at("api-limit-get-withdraw-permissions-by-giver").as<uint64_t>();
412  }
413  if(_options->count("api-limit-get-withdraw-permissions-by-recipient") > 0) {
414  _app_options.api_limit_get_withdraw_permissions_by_recipient =
415  _options->at("api-limit-get-withdraw-permissions-by-recipient").as<uint64_t>();
416  }
417  if(_options->count("api-limit-get-tickets") > 0) {
418  _app_options.api_limit_get_tickets =
419  _options->at("api-limit-get-tickets").as<uint64_t>();
420  }
421  if(_options->count("api-limit-get-liquidity-pools") > 0) {
422  _app_options.api_limit_get_liquidity_pools =
423  _options->at("api-limit-get-liquidity-pools").as<uint64_t>();
424  }
425  if(_options->count("api-limit-get-liquidity-pool-history") > 0) {
426  _app_options.api_limit_get_liquidity_pool_history =
427  _options->at("api-limit-get-liquidity-pool-history").as<uint64_t>();
428  }
429  if(_options->count("api-limit-get-samet-funds") > 0) {
430  _app_options.api_limit_get_samet_funds =
431  _options->at("api-limit-get-samet-funds").as<uint64_t>();
432  }
433  if(_options->count("api-limit-get-credit-offers") > 0) {
434  _app_options.api_limit_get_credit_offers =
435  _options->at("api-limit-get-credit-offers").as<uint64_t>();
436  }
437 }
438 
439 graphene::chain::genesis_state_type application_impl::initialize_genesis_state() const
440 {
441  try {
442  ilog("Initializing database...");
443  if( _options->count("genesis-json") > 0 )
444  {
445  std::string genesis_str;
446  fc::read_file_contents( _options->at("genesis-json").as<boost::filesystem::path>(), genesis_str );
447  auto genesis = fc::json::from_string( genesis_str ).as<graphene::chain::genesis_state_type>( 20 );
448  bool modified_genesis = false;
449  if( _options->count("genesis-timestamp") > 0 )
450  {
452  + genesis.initial_parameters.block_interval
453  + _options->at("genesis-timestamp").as<uint32_t>();
454  genesis.initial_timestamp -= ( genesis.initial_timestamp.sec_since_epoch()
455  % genesis.initial_parameters.block_interval );
456  modified_genesis = true;
457 
458  ilog(
459  "Used genesis timestamp: ${timestamp} (PLEASE RECORD THIS)",
460  ("timestamp", genesis.initial_timestamp.to_iso_string())
461  );
462  }
463  if( _options->count("dbg-init-key") > 0 )
464  {
465  std::string init_key = _options->at( "dbg-init-key" ).as<string>();
466  FC_ASSERT( genesis.initial_witness_candidates.size() >= genesis.initial_active_witnesses );
467  genesis.override_witness_signing_keys( init_key );
468  modified_genesis = true;
469  ilog("Set init witness key to ${init_key}", ("init_key", init_key));
470  }
471  if( modified_genesis )
472  {
473  wlog("WARNING: GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT");
474  genesis_str += "BOGUS";
475  }
476  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
477  return genesis;
478  }
479  else
480  {
481  std::string egenesis_json;
483  FC_ASSERT( egenesis_json != "" );
485  auto genesis = fc::json::from_string( egenesis_json ).as<graphene::chain::genesis_state_type>( 20 );
486  genesis.initial_chain_id = fc::sha256::hash( egenesis_json );
487  return genesis;
488  }
490 }
491 
492 void application_impl::open_chain_database() const
493 { try {
494  fc::create_directories(_data_dir / "blockchain");
495 
496  if( _options->count("resync-blockchain") > 0 )
497  _chain_db->wipe(_data_dir / "blockchain", true);
498 
499  flat_map<uint32_t,block_id_type> loaded_checkpoints;
500  if( _options->count("checkpoint") > 0 )
501  {
502  auto cps = _options->at("checkpoint").as<vector<string>>();
503  loaded_checkpoints.reserve( cps.size() );
504  for( auto cp : cps )
505  {
506  auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >( 2 );
507  loaded_checkpoints[item.first] = item.second;
508  }
509  }
510  _chain_db->add_checkpoints( loaded_checkpoints );
511 
512  if( _options->count("enable-standby-votes-tracking") > 0 )
513  {
514  _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as<bool>() );
515  }
516 
517  if( _options->count("replay-blockchain") > 0 || _options->count("revalidate-blockchain") > 0 )
518  _chain_db->wipe( _data_dir / "blockchain", false );
519 
520  try
521  {
522  // these flags are used in open() only, i. e. during replay
523  uint32_t skip;
524  if( _options->count("revalidate-blockchain") > 0 ) // see also handle_block()
525  {
526  if( !loaded_checkpoints.empty() )
527  wlog( "Warning - revalidate will not validate before last checkpoint" );
528  if( _options->count("force-validate") > 0 )
530  else
532  }
533  else // no revalidate, skip most checks
541 
542  auto genesis_loader = [this](){
543  return initialize_genesis_state();
544  };
545 
546  graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this, &genesis_loader] () {
547  _chain_db->open( _data_dir / "blockchain", genesis_loader, GRAPHENE_CURRENT_DB_VERSION );
548  });
549  }
550  catch( const fc::exception& e )
551  {
552  elog( "Caught exception ${e} in open(), you might want to force a replay", ("e", e.to_detail_string()) );
553  throw;
554  }
555 } FC_LOG_AND_RETHROW() }
556 
558 { try {
559  bool enable_p2p_network = true;
560  if( _options->count("enable-p2p-network") > 0 )
561  enable_p2p_network = _options->at("enable-p2p-network").as<bool>();
562 
563  open_chain_database();
564 
565  startup_plugins();
566 
567  if( enable_p2p_network && _active_plugins.find( "delayed_node" ) == _active_plugins.end() )
568  reset_p2p_node(_data_dir);
569 
570  reset_websocket_server();
571  reset_websocket_tls_server();
572 } FC_LOG_AND_RETHROW() }
573 
575 {
577  auto it = _apiaccess.permission_map.find(username);
578  if( it == _apiaccess.permission_map.end() )
579  {
580  it = _apiaccess.permission_map.find("*");
581  if( it == _apiaccess.permission_map.end() )
582  return result;
583  }
584  return it->second;
585 }
586 
587 void application_impl::set_api_access_info(const string& username, api_access_info&& permissions)
588 {
589  _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions)));
590 }
591 
592 bool application_impl::is_plugin_enabled(const string& name) const
593 {
594  return !(_active_plugins.find(name) == _active_plugins.end());
595 }
596 
601 {
602  try
603  {
604  if( id.item_type == graphene::net::block_message_type )
605  return _chain_db->is_known_block(id.item_hash);
606  else
607  return _chain_db->is_known_transaction(id.item_hash);
608  }
609  FC_CAPTURE_AND_RETHROW( (id) )
610 }
611 
621  std::vector<graphene::net::message_hash_type>& contained_transaction_msg_ids)
622 { try {
623 
624  auto latency = fc::time_point::now() - blk_msg.block.timestamp;
625  if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
626  {
627  const auto& witness = blk_msg.block.witness(*_chain_db);
628  const auto& witness_account = witness.witness_account(*_chain_db);
629  auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;
630  ilog("Got block: #${n} ${bid} time: ${t} transaction(s): ${x} "
631  "latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
632  ("t",blk_msg.block.timestamp)
633  ("n", blk_msg.block.block_num())
634  ("bid", blk_msg.block.id())
635  ("x", blk_msg.block.transactions.size())
636  ("l", (latency.count()/1000))
637  ("w",witness_account.name)
638  ("i",last_irr)("d",blk_msg.block.block_num()-last_irr) );
639  }
640  GRAPHENE_ASSERT( latency.count()/1000 > -2500, // 2.5 seconds
641  graphene::net::block_timestamp_in_future_exception,
642  "Rejecting block with timestamp in the future", );
643 
644  try {
645  const uint32_t skip = (_is_block_producer || _force_validate) ?
647  bool result = valve.do_serial( [this,&blk_msg,skip] () {
648  _chain_db->precompute_parallel( blk_msg.block, skip ).wait();
649  }, [this,&blk_msg,skip] () {
650  // TODO: in the case where this block is valid but on a fork that's too old for us to switch to,
651  // you can help the network code out by throwing a block_older_than_undo_history exception.
652  // when the net code sees that, it will stop trying to push blocks from that chain, but
653  // leave that peer connected so that they can get sync blocks from us
654  return _chain_db->push_block( blk_msg.block, skip );
655  });
656 
657  // the block was accepted, so we now know all of the transactions contained in the block
658  if (!sync_mode)
659  {
660  // if we're not in sync mode, there's a chance we will be seeing some transactions
661  // included in blocks before we see the free-floating transaction itself. If that
662  // happens, there's no reason to fetch the transactions, so construct a list of the
663  // transaction message ids we no longer need.
664  // during sync, it is unlikely that we'll see any old
665  contained_transaction_msg_ids.reserve( contained_transaction_msg_ids.size()
666  + blk_msg.block.transactions.size() );
667  for (const processed_transaction& ptrx : blk_msg.block.transactions)
668  {
669  graphene::net::trx_message transaction_message(ptrx);
670  contained_transaction_msg_ids.emplace_back(graphene::net::message(transaction_message).id());
671  }
672  }
673 
674  return result;
675  } catch ( const graphene::chain::unlinkable_block_exception& e ) {
676  // translate to a graphene::net exception
677  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
678  FC_THROW_EXCEPTION( graphene::net::unlinkable_block_exception,
679  "Error when pushing block:\n${e}",
680  ("e", e.to_detail_string()) );
681  } catch( const fc::exception& e ) {
682  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
683  throw;
684  }
685 
686  if( !_is_finished_syncing && !sync_mode )
687  {
688  _is_finished_syncing = true;
689  _self.syncing_finished();
690  }
691 } FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) return false; }
692 
694 { try {
695  static fc::time_point last_call;
696  static int trx_count = 0;
697  ++trx_count;
698  auto now = fc::time_point::now();
699  if( now - last_call > fc::seconds(1) ) {
700  ilog("Got ${c} transactions from network", ("c",trx_count) );
701  last_call = now;
702  trx_count = 0;
703  }
704 
705  _chain_db->precompute_parallel( transaction_message.trx ).wait();
706  _chain_db->push_transaction( transaction_message.trx );
707 } FC_CAPTURE_AND_RETHROW( (transaction_message) ) }
708 
709 void application_impl::handle_message(const message& message_to_process)
710 {
711  // not a transaction, not a block
712  FC_THROW( "Invalid Message Type" );
713 }
714 
716 {
717  uint32_t block_num = block_header::num_from_id(block_id);
718  block_id_type block_id_in_preferred_chain = _chain_db->get_block_id_for_num(block_num);
719  return block_id == block_id_in_preferred_chain;
720 }
721 
731 std::vector<item_hash_t> application_impl::get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
732  uint32_t& remaining_item_count,
733  uint32_t limit)
734 { try {
735  vector<block_id_type> result;
736  remaining_item_count = 0;
737  if( _chain_db->head_block_num() == 0 )
738  return result;
739 
740  result.reserve(limit);
741  block_id_type last_known_block_id;
742 
743  if (blockchain_synopsis.empty() ||
744  (blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
745  {
746  // peer has sent us an empty synopsis meaning they have no blocks.
747  // A bug in old versions would cause them to send a synopsis containing block 000000000
748  // when they had an empty blockchain, so pretend they sent the right thing here.
749 
750  // do nothing, leave last_known_block_id set to zero
751  }
752  else
753  {
754  bool found_a_block_in_synopsis = false;
755  for (const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis))
756  if (block_id_in_synopsis == block_id_type() ||
757  (_chain_db->is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis)))
758  {
759  last_known_block_id = block_id_in_synopsis;
760  found_a_block_in_synopsis = true;
761  break;
762  }
763  if (!found_a_block_in_synopsis)
764  FC_THROW_EXCEPTION( graphene::net::peer_is_on_an_unreachable_fork,
765  "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis" );
766  }
767  for( uint32_t num = block_header::num_from_id(last_known_block_id);
768  num <= _chain_db->head_block_num() && result.size() < limit;
769  ++num )
770  if( num > 0 )
771  result.push_back(_chain_db->get_block_id_for_num(num));
772 
773  if( !result.empty() && block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
774  remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
775 
776  return result;
777 } FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }
778 
783 { try {
784  // ilog("Request for item ${id}", ("id", id));
785  if( id.item_type == graphene::net::block_message_type )
786  {
787  auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
788  if( !opt_block )
789  elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}",
790  ("id", id.item_hash)("id2", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));
791  FC_ASSERT( opt_block.valid() );
792  // ilog("Serving up block #${num}", ("num", opt_block->block_num()));
793  return block_message(std::move(*opt_block));
794  }
795  return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
796 } FC_CAPTURE_AND_RETHROW( (id) ) }
797 
799 {
800  return _chain_db->get_chain_id();
801 }
802 
861 std::vector<item_hash_t> application_impl::get_blockchain_synopsis(const item_hash_t& reference_point,
862  uint32_t number_of_blocks_after_reference_point)
863 { try {
864  std::vector<item_hash_t> synopsis;
865  synopsis.reserve(30);
866  uint32_t high_block_num;
867  uint32_t non_fork_high_block_num;
868  uint32_t low_block_num = _chain_db->last_non_undoable_block_num();
869  std::vector<block_id_type> fork_history;
870 
871  if (reference_point != item_hash_t())
872  {
873  // the node is asking for a summary of the block chain up to a specified
874  // block, which may or may not be on a fork
875  // for now, assume it's not on a fork
876  if (is_included_block(reference_point))
877  {
878  // reference_point is a block we know about and is on the main chain
879  uint32_t reference_point_block_num = block_header::num_from_id(reference_point);
880  assert(reference_point_block_num > 0);
881  high_block_num = reference_point_block_num;
882  non_fork_high_block_num = high_block_num;
883 
884  if (reference_point_block_num < low_block_num)
885  {
886  // we're on the same fork (at least as far as reference_point) but we've passed
887  // reference point and could no longer undo that far if we diverged after that
888  // block. This should probably only happen due to a race condition where
889  // the network thread calls this function, and then immediately pushes a bunch of blocks,
890  // then the main thread finally processes this function.
891  // with the current framework, there's not much we can do to tell the network
892  // thread what our current head block is, so we'll just pretend that
893  // our head is actually the reference point.
894  // this *may* enable us to fetch blocks that we're unable to push, but that should
895  // be a rare case (and correctly handled)
896  low_block_num = reference_point_block_num;
897  }
898  }
899  else
900  {
901  // block is a block we know about, but it is on a fork
902  try
903  {
904  fork_history = _chain_db->get_block_ids_on_fork(reference_point);
905  // returns a vector where the last element is the common ancestor with the preferred chain,
906  // and the first element is the reference point you passed in
907  assert(fork_history.size() >= 2);
908 
909  if( fork_history.front() != reference_point )
910  {
911  edump( (fork_history)(reference_point) );
912  assert(fork_history.front() == reference_point);
913  }
914  block_id_type last_non_fork_block = fork_history.back();
915  fork_history.pop_back(); // remove the common ancestor
916  boost::reverse(fork_history);
917 
918  if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?)
919  non_fork_high_block_num = 0;
920  else
921  non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);
922 
923  high_block_num = non_fork_high_block_num + fork_history.size();
924  assert(high_block_num == block_header::num_from_id(fork_history.back()));
925  }
926  catch (const fc::exception& e)
927  {
928  // unable to get fork history for some reason. maybe not linked?
929  // we can't return a synopsis of its chain
930  elog( "Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}",
931  ("hash", reference_point)("exception", e) );
932  throw;
933  }
934  if (non_fork_high_block_num < low_block_num)
935  {
936  wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
937  "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
938  ("low_block_num", low_block_num)
939  ("non_fork_high_block_num", non_fork_high_block_num));
940  FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
941  }
942  }
943  }
944  else
945  {
946  // no reference point specified, summarize the whole block chain
947  high_block_num = _chain_db->head_block_num();
948  non_fork_high_block_num = high_block_num;
949  if (high_block_num == 0)
950  return synopsis; // we have no blocks
951  }
952 
953  if( low_block_num == 0)
954  low_block_num = 1;
955 
956  // at this point:
957  // low_block_num is the block before the first block we can undo,
958  // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
959  // high_block_num is the block number of the reference block, or the end of the chain if no reference provided
960 
961  // true_high_block_num is the ending block number after the network code appends any item ids it
962  // knows about that we don't
963  uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
964  do
965  {
966  // for each block in the synopsis, figure out where to pull the block id from.
967  // if it's <= non_fork_high_block_num, we grab it from the main blockchain;
968  // if it's not, we pull it from the fork history
969  if (low_block_num <= non_fork_high_block_num)
970  synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));
971  else
972  synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);
973  low_block_num += (true_high_block_num - low_block_num + 2) / 2;
974  }
975  while (low_block_num <= high_block_num);
976 
977  //idump((synopsis));
978  return synopsis;
980 
988 void application_impl::sync_status(uint32_t item_type, uint32_t item_count)
989 {
990  // any status reports to GUI go here
991 }
992 
997 {
998  // any status reports to GUI go here
999 }
1000 
1002 { try {
1003  return block_header::num_from_id(block_id);
1004 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
1005 
1011 { try {
1012  auto opt_block = _chain_db->fetch_block_by_id( block_id );
1013  if( opt_block.valid() ) return opt_block->timestamp;
1014  return fc::time_point_sec::min();
1015 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
1016 
1018 {
1019  return _chain_db->head_block_id();
1020 }
1021 
1023 {
1024  return 0; // there are no forks in graphene
1025 }
1026 
1027 void application_impl::error_encountered(const std::string& message, const fc::oexception& error)
1028 {
1029  // notify GUI or something cool
1030 }
1031 
1033 {
1034  FC_ASSERT( _chain_db, "Chain database is not operational" );
1035  return _chain_db->get_global_properties().parameters.block_interval;
1036 }
1037 
1038 void application_impl::shutdown()
1039 {
1040  ilog( "Shutting down application" );
1041  if( _websocket_tls_server )
1042  _websocket_tls_server.reset();
1043  if( _websocket_server )
1044  _websocket_server.reset();
1045  // TODO wait until all connections are closed and messages handled?
1046 
1047  // plugins E.G. witness_plugin may send data to p2p network, so shutdown them first
1048  ilog( "Shutting down plugins" );
1049  shutdown_plugins();
1050 
1051  if( _p2p_network )
1052  {
1053  ilog( "Disconnecting from P2P network" );
1054  // FIXME wait() is called in close() but it doesn't block this thread
1055  _p2p_network->close();
1056  _p2p_network.reset();
1057  }
1058  else
1059  ilog( "P2P network is disabled" );
1060 
1061  if( _chain_db )
1062  {
1063  ilog( "Closing chain database" );
1064  _chain_db->close();
1065  _chain_db.reset();
1066  }
1067  else
1068  ilog( "Chain database is not open" );
1069 }
1070 
1071 void application_impl::enable_plugin( const string& name )
1072 {
1073  FC_ASSERT(_available_plugins[name], "Unknown plugin '" + name + "'");
1074  _active_plugins[name] = _available_plugins[name];
1075 }
1076 
1077 void application_impl::initialize_plugins() const
1078 {
1079  for( const auto& entry : _active_plugins )
1080  {
1081  ilog( "Initializing plugin ${name}", ( "name", entry.second->plugin_name() ) );
1082  entry.second->plugin_initialize( *_options );
1083  ilog( "Initialized plugin ${name}", ( "name", entry.second->plugin_name() ) );
1084  }
1085 }
1086 
1087 void application_impl::startup_plugins() const
1088 {
1089  for( const auto& entry : _active_plugins )
1090  {
1091  ilog( "Starting plugin ${name}", ( "name", entry.second->plugin_name() ) );
1092  entry.second->plugin_startup();
1093  ilog( "Started plugin ${name}", ( "name", entry.second->plugin_name() ) );
1094  }
1095 }
1096 
1097 void application_impl::shutdown_plugins() const
1098 {
1099  for( const auto& entry : _active_plugins )
1100  {
1101  ilog( "Stopping plugin ${name}", ( "name", entry.second->plugin_name() ) );
1102  entry.second->plugin_shutdown();
1103  ilog( "Stopped plugin ${name}", ( "name", entry.second->plugin_name() ) );
1104  }
1105 }
1106 
1107 void application_impl::add_available_plugin(std::shared_ptr<graphene::app::abstract_plugin> p)
1108 {
1109  _available_plugins[p->plugin_name()] = p;
1110 }
1111 
1112 void application_impl::set_block_production(bool producing_blocks)
1113 {
1114  _is_block_producer = producing_blocks;
1115 }
1116 
1117 } } } // namespace graphene namespace app namespace detail
1118 
1119 namespace graphene { namespace app {
1120 
1122  : my(std::make_shared<detail::application_impl>(*this))
1123 {
1124  //nothing else to do
1125 }
1126 
1128 {
1129  ilog("Application quitting");
1130  my->shutdown();
1131 }
1132 
1133 void application::set_program_options(boost::program_options::options_description& command_line_options,
1134  boost::program_options::options_description& configuration_file_options) const
1135 {
1136  const auto& default_opts = application_options::get_default();
1137  configuration_file_options.add_options()
1138  ("enable-p2p-network", bpo::value<bool>()->implicit_value(true),
1139  "Whether to enable P2P network. Note: if delayed_node plugin is enabled, "
1140  "this option will be ignored and P2P network will always be disabled.")
1141  ("p2p-endpoint", bpo::value<string>(), "Endpoint for P2P node to listen on")
1142  ("seed-node,s", bpo::value<vector<string>>()->composing(),
1143  "P2P nodes to connect to on startup (may specify multiple times)")
1144  ("seed-nodes", bpo::value<string>()->composing(),
1145  "JSON array of P2P nodes to connect to on startup")
1146  ("checkpoint,c", bpo::value<vector<string>>()->composing(),
1147  "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
1148  ("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"),
1149  "Endpoint for websocket RPC to listen on")
1150  ("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"),
1151  "Endpoint for TLS websocket RPC to listen on")
1152  ("server-pem,p", bpo::value<string>()->implicit_value("server.pem"),
1153  "The TLS certificate file for this server")
1154  ("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
1155  ("proxy-forwarded-for-header", bpo::value<string>()->implicit_value("X-Forwarded-For-Client"),
1156  "A HTTP header similar to X-Forwarded-For (XFF), used by the RPC server to extract clients' address info, "
1157  "usually added by a trusted reverse proxy")
1158  ("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
1159  ("dbg-init-key", bpo::value<string>(),
1160  "Block signing key to use for init witnesses, overrides genesis file, for debug")
1161  ("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
1162  ("io-threads", bpo::value<uint16_t>()->implicit_value(0),
1163  "Number of IO threads, default to 0 for auto-configuration")
1164  ("enable-subscribe-to-all", bpo::value<bool>()->implicit_value(true),
1165  "Whether allow API clients to subscribe to universal object creation and removal events")
1166  ("enable-standby-votes-tracking", bpo::value<bool>()->implicit_value(true),
1167  "Whether to enable tracking of votes of standby witnesses and committee members. "
1168  "Set it to true to provide accurate data to API clients, set to false for slightly better performance.")
1169  ("api-limit-get-account-history-operations",
1170  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_account_history_operations),
1171  "For history_api::get_account_history_operations to set max limit value")
1172  ("api-limit-get-account-history",
1173  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_account_history),
1174  "For history_api::get_account_history to set max limit value")
1175  ("api-limit-get-grouped-limit-orders",
1176  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_grouped_limit_orders),
1177  "For orders_api::get_grouped_limit_orders to set max limit value")
1178  ("api-limit-get-relative-account-history",
1179  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_relative_account_history),
1180  "For history_api::get_relative_account_history to set max limit value")
1181  ("api-limit-get-account-history-by-operations",
1182  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_account_history_by_operations),
1183  "For history_api::get_account_history_by_operations to set max limit value")
1184  ("api-limit-get-asset-holders",
1185  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_asset_holders),
1186  "For asset_api::get_asset_holders to set max limit value")
1187  ("api-limit-get-key-references",
1188  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_key_references),
1189  "For database_api_impl::get_key_references to set max limit value")
1190  ("api-limit-get-htlc-by",
1191  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_htlc_by),
1192  "For database_api_impl::get_htlc_by_from and get_htlc_by_to to set max limit value")
1193  ("api-limit-get-full-accounts",
1194  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_full_accounts),
1195  "For database_api_impl::get_full_accounts to set max accounts to query at once")
1196  ("api-limit-get-full-accounts-lists",
1197  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_full_accounts_lists),
1198  "For database_api_impl::get_full_accounts to set max items to return in the lists")
1199  ("api-limit-get-top-voters",
1200  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_top_voters),
1201  "For database_api_impl::get_top_voters to set max limit value")
1202  ("api-limit-get-call-orders",
1203  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_call_orders),
1204  "For database_api_impl::get_call_orders and get_call_orders_by_account to set max limit value")
1205  ("api-limit-get-settle-orders",
1206  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_settle_orders),
1207  "For database_api_impl::get_settle_orders and get_settle_orders_by_account to set max limit value")
1208  ("api-limit-get-assets",
1209  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_assets),
1210  "For database_api_impl::list_assets and get_assets_by_issuer to set max limit value")
1211  ("api-limit-get-limit-orders",
1212  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_limit_orders),
1213  "For database_api_impl::get_limit_orders to set max limit value")
1214  ("api-limit-get-limit-orders-by-account",
1215  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_limit_orders_by_account),
1216  "For database_api_impl::get_limit_orders_by_account to set max limit value")
1217  ("api-limit-get-order-book",
1218  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_order_book),
1219  "For database_api_impl::get_order_book to set max limit value")
1220  ("api-limit-list-htlcs",
1221  bpo::value<uint64_t>()->default_value(default_opts.api_limit_list_htlcs),
1222  "For database_api_impl::list_htlcs to set max limit value")
1223  ("api-limit-lookup-accounts",
1224  bpo::value<uint64_t>()->default_value(default_opts.api_limit_lookup_accounts),
1225  "For database_api_impl::lookup_accounts to set max limit value")
1226  ("api-limit-lookup-witness-accounts",
1227  bpo::value<uint64_t>()->default_value(default_opts.api_limit_lookup_witness_accounts),
1228  "For database_api_impl::lookup_witness_accounts to set max limit value")
1229  ("api-limit-lookup-committee-member-accounts",
1230  bpo::value<uint64_t>()->default_value(default_opts.api_limit_lookup_committee_member_accounts),
1231  "For database_api_impl::lookup_committee_member_accounts to set max limit value")
1232  ("api-limit-lookup-vote-ids",
1233  bpo::value<uint64_t>()->default_value(default_opts.api_limit_lookup_vote_ids),
1234  "For database_api_impl::lookup_vote_ids to set max limit value")
1235  ("api-limit-get-account-limit-orders",
1236  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_account_limit_orders),
1237  "For database_api_impl::get_account_limit_orders to set max limit value")
1238  ("api-limit-get-collateral-bids",
1239  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_collateral_bids),
1240  "For database_api_impl::get_collateral_bids to set max limit value")
1241  ("api-limit-get-top-markets",
1242  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_top_markets),
1243  "For database_api_impl::get_top_markets to set max limit value")
1244  ("api-limit-get-trade-history",
1245  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_trade_history),
1246  "For database_api_impl::get_trade_history to set max limit value")
1247  ("api-limit-get-trade-history-by-sequence",
1248  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_trade_history_by_sequence),
1249  "For database_api_impl::get_trade_history_by_sequence to set max limit value")
1250  ("api-limit-get-withdraw-permissions-by-giver",
1251  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_withdraw_permissions_by_giver),
1252  "For database_api_impl::get_withdraw_permissions_by_giver to set max limit value")
1253  ("api-limit-get-withdraw-permissions-by-recipient",
1254  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_withdraw_permissions_by_recipient),
1255  "For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value")
1256  ("api-limit-get-tickets",
1257  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_tickets),
1258  "Set maximum limit value for database APIs which query for tickets")
1259  ("api-limit-get-liquidity-pools",
1260  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_liquidity_pools),
1261  "Set maximum limit value for database APIs which query for liquidity pools")
1262  ("api-limit-get-liquidity-pool-history",
1263  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_liquidity_pool_history),
1264  "Set maximum limit value for APIs which query for history of liquidity pools")
1265  ("api-limit-get-samet-funds",
1266  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_samet_funds),
1267  "Set maximum limit value for database APIs which query for SameT Funds")
1268  ("api-limit-get-credit-offers",
1269  bpo::value<uint64_t>()->default_value(default_opts.api_limit_get_credit_offers),
1270  "Set maximum limit value for database APIs which query for credit offers or credit deals")
1271  ;
1272  command_line_options.add(configuration_file_options);
1273  command_line_options.add_options()
1274  ("replay-blockchain", "Rebuild object graph by replaying all blocks without validation")
1275  ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation")
1276  ("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
1277  ("force-validate", "Force validation of all transactions during normal operation")
1278  ("genesis-timestamp", bpo::value<uint32_t>(),
1279  "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)")
1280  ;
1281  command_line_options.add(_cli_options);
1282  configuration_file_options.add(_cfg_options);
1283 }
1284 
1285 void application::initialize(const fc::path& data_dir,
1286  std::shared_ptr<boost::program_options::variables_map> options) const
1287 {
1288  ilog( "Initializing application" );
1289  my->initialize( data_dir, options );
1290  ilog( "Done initializing application" );
1291 }
1292 
1294 {
1295  try {
1296  ilog( "Starting up application" );
1297  my->startup();
1298  ilog( "Done starting up application" );
1299  } catch ( const fc::exception& e ) {
1300  elog( "${e}", ("e",e.to_detail_string()) );
1301  throw;
1302  } catch ( ... ) {
1303  elog( "unexpected exception" );
1304  throw;
1305  }
1306 }
1307 
1309 {
1310  try {
1311  my->set_api_limit();
1312  } catch ( const fc::exception& e ) {
1313  elog( "${e}", ("e",e.to_detail_string()) );
1314  throw;
1315  } catch ( ... ) {
1316  elog( "unexpected exception" );
1317  throw;
1318  }
1319 }
1320 std::shared_ptr<abstract_plugin> application::get_plugin(const string& name) const
1321 {
1322  return my->_active_plugins[name];
1323 }
1324 
1325 bool application::is_plugin_enabled(const string& name) const
1326 {
1327  return my->is_plugin_enabled(name);
1328 }
1329 
1331 {
1332  return my->_p2p_network;
1333 }
1334 
1335 std::shared_ptr<chain::database> application::chain_database() const
1336 {
1337  return my->_chain_db;
1338 }
1339 
1340 void application::set_block_production(bool producing_blocks)
1341 {
1342  my->set_block_production(producing_blocks);
1343 }
1344 
1346 {
1347  return my->get_api_access_info( username );
1348 }
1349 
1350 void application::set_api_access_info(const string& username, api_access_info&& permissions)
1351 {
1352  my->set_api_access_info(username, std::move(permissions));
1353 }
1354 
1356 {
1357  return my->_is_finished_syncing;
1358 }
1359 
1360 void application::enable_plugin(const string& name) const
1361 {
1362  my->enable_plugin(name);
1363 }
1364 
1365 void application::add_available_plugin(std::shared_ptr<graphene::app::abstract_plugin> p) const
1366 {
1367  my->add_available_plugin(p);
1368 }
1369 
1371 {
1372  return my->_app_options;
1373 }
1374 
1375 // namespace detail
1376 } }
std::vector< graphene::net::item_hash_t > get_block_ids(const std::vector< graphene::net::item_hash_t > &blockchain_synopsis, uint32_t &remaining_item_count, uint32_t limit) override
fc::optional< api_access_info > get_api_access_info(const string &username) const
void enable_plugin(const string &name)
Enables a plugin.
const std::string GRAPHENE_CURRENT_DB_VERSION
Definition: config.hpp:35
void add_available_plugin(std::shared_ptr< abstract_plugin > p)
Add an available plugin.
bool exists(const path &p)
Definition: filesystem.cpp:209
bool handle_block(const graphene::net::block_message &blk_msg, bool sync_mode, std::vector< graphene::net::message_hash_type > &contained_transaction_msg_ids) override
allows the application to validate an item prior to broadcasting to peers.
fc::time_point_sec timestamp
Definition: block.hpp:35
void enable_plugin(const string &name) const
fc::optional< api_access_info > get_api_access_info(const string &username) const
#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT
Definition: config.hpp:77
std::shared_ptr< chain::database > chain_database() const
bool has_item(const net::item_id &id) override
static uint32_t num_from_id(const block_id_type &id)
Definition: block.cpp:36
bool is_plugin_enabled(const string &name) const
graphene::chain::genesis_state_type create_example_genesis()
Definition: application.cpp:79
void set_program_options(boost::program_options::options_description &command_line_options, boost::program_options::options_description &configuration_file_options) const
void set_block_production(bool producing_blocks)
#define GRAPHENE_NET_MAX_NESTED_OBJECTS
Definition: config.hpp:112
void initialize(const fc::path &data_dir, std::shared_ptr< boost::program_options::variables_map > options)
static const application_options & get_default()
Definition: application.hpp:81
void error_encountered(const std::string &message, const fc::oexception &error) override
Definition: api.cpp:56
#define elog(FORMAT,...)
Definition: logger.hpp:129
bool is_included_block(const graphene::chain::block_id_type &block_id)
uint32_t get_block_number(const graphene::net::item_hash_t &block_id) override
void initialize(const fc::path &data_dir, std::shared_ptr< boost::program_options::variables_map > options) const
void connection_count_changed(uint32_t c) override
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
std::shared_ptr< node > node_ptr
Definition: node.hpp:319
vector< initial_balance_type > initial_balances
void with_skip_flags(database &db, uint32_t skip_flags, Lambda callback)
Definition: db_with.hpp:113
void create_directories(const path &p)
Definition: filesystem.cpp:210
void read_file_contents(const fc::path &filename, std::string &result)
Definition: fstream.cpp:107
vector< initial_witness_type > initial_witness_candidates
#define wlog(FORMAT,...)
Definition: logger.hpp:123
static variant from_file(const fc::path &p, parse_type ptype=legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:767
std::shared_ptr< websocket_connection > websocket_connection_ptr
Definition: websocket.hpp:47
std::shared_ptr< abstract_plugin > get_plugin(const string &name) const
fc::ripemd160 item_hash_t
fc::ripemd160 block_id_type
Definition: types.hpp:269
static sha256 hash(const char *d, uint32_t dlen)
Definition: sha256.cpp:41
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
graphene::protocol::precomputable_transaction trx
std::string key_to_wif(const fc::sha256 &private_secret)
uint32_t block_num() const
Definition: block.hpp:34
vector< initial_account_type > initial_accounts
static const fee_schedule & get_default()
void set_api_access_info(const string &username, api_access_info &&permissions)
#define FC_THROW( ...)
Definition: exception.hpp:366
void set_block_production(bool producing_blocks)
#define edump(SEQ)
Definition: logger.hpp:182
microseconds seconds(int64_t s)
Definition: time.hpp:34
#define ilog(FORMAT,...)
Definition: logger.hpp:117
graphene::net::item_hash_t get_head_block_id() const override
const block_id_type & id() const
Definition: block.cpp:41
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
T as(uint32_t max_depth) const
Definition: variant.hpp:337
void handle_transaction(const graphene::net::trx_message &transaction_message) override
Called when a new transaction comes in from the network.
static private_key regenerate(const fc::sha256 &secret)
std::vector< std::string > allowed_apis
Definition: api_access.hpp:38
uint8_t get_current_block_interval_in_seconds() const override
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:379
static endpoint from_string(const string &s)
Definition: ip.cpp:74
std::vector< graphene::net::item_hash_t > get_blockchain_synopsis(const graphene::net::item_hash_t &reference_point, uint32_t number_of_blocks_after_reference_point) override
void set_api_access_info(const string &username, api_access_info &&permissions)
fc::time_point_sec get_block_time(const graphene::net::item_hash_t &block_id) override
used while reindexing – note this skips expiration check too
Definition: database.hpp:84
vector< initial_committee_member_type > initial_committee_candidates
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
#define dlog(FORMAT,...)
Definition: logger.hpp:100
const application_options & get_options()
std::string to_string(double)
Definition: string.cpp:73
fc::sha256 get_egenesis_json_hash()
uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override
void handle_message(const graphene::net::message &message_to_process) override
Called when a new message comes in from the network other than a block or a transaction. Currently there are no other possible messages, so this should never be called.
std::string base64_decode(const std::string &encoded_string)
Definition: base64.cpp:96
static time_point_sec min()
Definition: time.hpp:87
static variant from_string(const string &utf8_str, parse_type ptype=legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:458
constexpr int64_t GRAPHENE_MAX_SHARE_SUPPLY(1000000000000000LL)
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
void sync_status(uint32_t item_type, uint32_t item_count) override
void new_connection(const fc::http::websocket_connection_ptr &c)
#define GRAPHENE_SYMBOL
Definition: config.hpp:26
graphene::net::message get_item(const graphene::net::item_id &id) override
static time_point now()
Definition: time.cpp:13
wraps boost::filesystem::path to provide platform independent path manipulation.
Definition: filesystem.hpp:28
#define FC_LOG_AND_RETHROW()
Definition: exception.hpp:395
vector< processed_transaction > transactions
Definition: block.hpp:63
void reset_p2p_node(const fc::path &data_dir)
void compute_egenesis_json(std::string &result)
captures the result of evaluating the operations contained in the transaction
graphene::chain::chain_id_type get_chain_id() const override
uint8_t block_interval
interval in seconds between blocks
static void set_num_threads(uint16_t num_threads)
Definition: asio.cpp:103
bool is_plugin_enabled(const string &name) const
Returns whether a plugin is enabled.
used when applying locally generated transactions
Definition: database.hpp:83