BitShares-Core  5.0.0
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 { try {
119  _p2p_network = std::make_shared<net::node>("BitShares Reference Implementation");
120 
121  _p2p_network->load_configuration(data_dir / "p2p");
122  _p2p_network->set_node_delegate(this);
123 
124  if( _options->count("seed-node") )
125  {
126  auto seeds = _options->at("seed-node").as<vector<string>>();
127  _p2p_network->add_seed_nodes(seeds);
128  }
129 
130  if( _options->count("seed-nodes") )
131  {
132  auto seeds_str = _options->at("seed-nodes").as<string>();
133  auto seeds = fc::json::from_string(seeds_str).as<vector<string>>(2);
134  _p2p_network->add_seed_nodes(seeds);
135  }
136  else
137  {
138  // https://bitsharestalk.org/index.php/topic,23715.0.html
139  vector<string> seeds = {
140  #include "../egenesis/seed-nodes.txt"
141  };
142  _p2p_network->add_seed_nodes(seeds);
143  }
144 
145  if( _options->count("p2p-endpoint") )
146  _p2p_network->listen_on_endpoint(fc::ip::endpoint::from_string(_options->at("p2p-endpoint").as<string>()), true);
147  else
148  _p2p_network->listen_on_port(0, false);
149  _p2p_network->listen_to_p2p_network();
150  ilog("Configured p2p node to listen on ${ip}", ("ip", _p2p_network->get_actual_listening_endpoint()));
151 
152  _p2p_network->connect_to_p2p_network();
154  _chain_db->head_block_id()),
155  std::vector<uint32_t>());
157 
159 {
160  auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_NET_MAX_NESTED_OBJECTS);
161  auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );
162  login->enable_api("database_api");
163 
164  wsc->register_api(login->database());
165  wsc->register_api(fc::api<graphene::app::login_api>(login));
166  c->set_session_data( wsc );
167 
168  std::string username = "*";
169  std::string password = "*";
170 
171  // Try to extract login information from "Authorization" header if present
172  std::string auth = c->get_request_header("Authorization");
173  if( boost::starts_with(auth, "Basic ") ) {
174 
175  FC_ASSERT( auth.size() > 6 );
176  auto user_pass = fc::base64_decode(auth.substr(6));
177 
178  std::vector<std::string> parts;
179  boost::split( parts, user_pass, boost::is_any_of(":") );
180 
181  FC_ASSERT(parts.size() == 2);
182 
183  username = parts[0];
184  password = parts[1];
185  }
186 
187  login->login(username, password);
188 }
189 
191 { try {
192  if( !_options->count("rpc-endpoint") )
193  return;
194 
195  string proxy_forward_header;
196  if( _options->count("proxy-forwarded-for-header") )
197  proxy_forward_header = _options->at("proxy-forwarded-for-header").as<string>();
198 
199  _websocket_server = std::make_shared<fc::http::websocket_server>( proxy_forward_header );
200  _websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
201 
202  ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
203  _websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
204  _websocket_server->start_accept();
206 
208 { try {
209  if( !_options->count("rpc-tls-endpoint") )
210  return;
211  if( !_options->count("server-pem") )
212  {
213  wlog( "Please specify a server-pem to use rpc-tls-endpoint" );
214  return;
215  }
216 
217  string proxy_forward_header;
218  if( _options->count("proxy-forwarded-for-header") )
219  proxy_forward_header = _options->at("proxy-forwarded-for-header").as<string>();
220 
221  string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
222  _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(
223  _options->at("server-pem").as<string>(), password, proxy_forward_header );
224  _websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
225 
226  ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
227  _websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
228  _websocket_tls_server->start_accept();
230 
231 void application_impl::set_dbg_init_key( graphene::chain::genesis_state_type& genesis, const std::string& init_key )
232 {
233  flat_set< std::string > initial_witness_names;
234  public_key_type init_pubkey( init_key );
235  for( uint64_t i=0; i<genesis.initial_active_witnesses; i++ )
236  genesis.initial_witness_candidates[i].block_signing_key = init_pubkey;
237 }
238 
239 
240 
242  if (_options->count("api-limit-get-account-history-operations")) {
243  _app_options.api_limit_get_account_history_operations = _options->at("api-limit-get-account-history-operations").as<uint64_t>();
244  }
245  if(_options->count("api-limit-get-account-history")){
246  _app_options.api_limit_get_account_history = _options->at("api-limit-get-account-history").as<uint64_t>();
247  }
248  if(_options->count("api-limit-get-grouped-limit-orders")){
249  _app_options.api_limit_get_grouped_limit_orders = _options->at("api-limit-get-grouped-limit-orders").as<uint64_t>();
250  }
251  if(_options->count("api-limit-get-relative-account-history")){
252  _app_options.api_limit_get_relative_account_history = _options->at("api-limit-get-relative-account-history").as<uint64_t>();
253  }
254  if(_options->count("api-limit-get-account-history-by-operations")){
255  _app_options.api_limit_get_account_history_by_operations = _options->at("api-limit-get-account-history-by-operations").as<uint64_t>();
256  }
257  if(_options->count("api-limit-get-asset-holders")){
258  _app_options.api_limit_get_asset_holders = _options->at("api-limit-get-asset-holders").as<uint64_t>();
259  }
260  if(_options->count("api-limit-get-key-references")){
261  _app_options.api_limit_get_key_references = _options->at("api-limit-get-key-references").as<uint64_t>();
262  }
263  if(_options->count("api-limit-get-htlc-by")) {
264  _app_options.api_limit_get_htlc_by = _options->at("api-limit-get-htlc-by").as<uint64_t>();
265  }
266  if(_options->count("api-limit-get-full-accounts")) {
267  _app_options.api_limit_get_full_accounts = _options->at("api-limit-get-full-accounts").as<uint64_t>();
268  }
269  if(_options->count("api-limit-get-full-accounts-lists")) {
270  _app_options.api_limit_get_full_accounts_lists = _options->at("api-limit-get-full-accounts-lists").as<uint64_t>();
271  }
272  if(_options->count("api-limit-get-top-voters")) {
273  _app_options.api_limit_get_top_voters = _options->at("api-limit-get-top-voters").as<uint64_t>();
274  }
275  if(_options->count("api-limit-get-call-orders")) {
276  _app_options.api_limit_get_call_orders = _options->at("api-limit-get-call-orders").as<uint64_t>();
277  }
278  if(_options->count("api-limit-get-settle-orders")) {
279  _app_options.api_limit_get_settle_orders = _options->at("api-limit-get-settle-orders").as<uint64_t>();
280  }
281  if(_options->count("api-limit-get-assets")) {
282  _app_options.api_limit_get_assets = _options->at("api-limit-get-assets").as<uint64_t>();
283  }
284  if(_options->count("api-limit-get-limit-orders")){
285  _app_options.api_limit_get_limit_orders = _options->at("api-limit-get-limit-orders").as<uint64_t>();
286  }
287  if(_options->count("api-limit-get-limit-orders-by-account")){
288  _app_options.api_limit_get_limit_orders_by_account = _options->at("api-limit-get-limit-orders-by-account").as<uint64_t>();
289  }
290  if(_options->count("api-limit-get-order-book")){
291  _app_options.api_limit_get_order_book = _options->at("api-limit-get-order-book").as<uint64_t>();
292  }
293  if(_options->count("api-limit-list-htlcs")){
294  _app_options.api_limit_list_htlcs = _options->at("api-limit-list-htlcs").as<uint64_t>();
295  }
296  if(_options->count("api-limit-lookup-accounts")) {
297  _app_options.api_limit_lookup_accounts = _options->at("api-limit-lookup-accounts").as<uint64_t>();
298  }
299  if(_options->count("api-limit-lookup-witness-accounts")) {
300  _app_options.api_limit_lookup_witness_accounts = _options->at("api-limit-lookup-witness-accounts").as<uint64_t>();
301  }
302  if(_options->count("api-limit-lookup-committee-member-accounts")) {
303  _app_options.api_limit_lookup_committee_member_accounts = _options->at("api-limit-lookup-committee-member-accounts").as<uint64_t>();
304  }
305  if(_options->count("api-limit-lookup-vote-ids")) {
306  _app_options.api_limit_lookup_vote_ids = _options->at("api-limit-lookup-vote-ids").as<uint64_t>();
307  }
308  if(_options->count("api-limit-get-account-limit-orders")) {
309  _app_options.api_limit_get_account_limit_orders = _options->at("api-limit-get-account-limit-orders").as<uint64_t>();
310  }
311  if(_options->count("api-limit-get-collateral-bids")) {
312  _app_options.api_limit_get_collateral_bids = _options->at("api-limit-get-collateral-bids").as<uint64_t>();
313  }
314  if(_options->count("api-limit-get-top-markets")) {
315  _app_options.api_limit_get_top_markets = _options->at("api-limit-get-top-markets").as<uint64_t>();
316  }
317  if(_options->count("api-limit-get-trade-history")) {
318  _app_options.api_limit_get_trade_history = _options->at("api-limit-get-trade-history").as<uint64_t>();
319  }
320  if(_options->count("api-limit-get-trade-history-by-sequence")) {
321  _app_options.api_limit_get_trade_history_by_sequence = _options->at("api-limit-get-trade-history-by-sequence").as<uint64_t>();
322  }
323  if(_options->count("api-limit-get-withdraw-permissions-by-giver")) {
324  _app_options.api_limit_get_withdraw_permissions_by_giver = _options->at("api-limit-get-withdraw-permissions-by-giver").as<uint64_t>();
325  }
326  if(_options->count("api-limit-get-withdraw-permissions-by-recipient")) {
327  _app_options.api_limit_get_withdraw_permissions_by_recipient = _options->at("api-limit-get-withdraw-permissions-by-recipient").as<uint64_t>();
328  }
329  if(_options->count("api-limit-get-liquidity-pools")) {
330  _app_options.api_limit_get_liquidity_pools = _options->at("api-limit-get-liquidity-pools").as<uint64_t>();
331  }
332 }
333 
335 { try {
336  fc::create_directories(_data_dir / "blockchain");
337 
338  auto initial_state = [this] {
339  ilog("Initializing database...");
340  if( _options->count("genesis-json") )
341  {
342  std::string genesis_str;
343  fc::read_file_contents( _options->at("genesis-json").as<boost::filesystem::path>(), genesis_str );
345  bool modified_genesis = false;
346  if( _options->count("genesis-timestamp") )
347  {
350  + _options->at("genesis-timestamp").as<uint32_t>();
353  modified_genesis = true;
354 
355  ilog(
356  "Used genesis timestamp: ${timestamp} (PLEASE RECORD THIS)",
357  ("timestamp", genesis.initial_timestamp.to_iso_string())
358  );
359  }
360  if( _options->count("dbg-init-key") )
361  {
362  std::string init_key = _options->at( "dbg-init-key" ).as<string>();
364  set_dbg_init_key( genesis, init_key );
365  modified_genesis = true;
366  ilog("Set init witness key to ${init_key}", ("init_key", init_key));
367  }
368  if( modified_genesis )
369  {
370  wlog("WARNING: GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT");
371  genesis_str += "BOGUS";
372  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
373  }
374  else
375  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
376  return genesis;
377  }
378  else
379  {
380  std::string egenesis_json;
382  FC_ASSERT( egenesis_json != "" );
384  auto genesis = fc::json::from_string( egenesis_json ).as<graphene::chain::genesis_state_type>( 20 );
385  genesis.initial_chain_id = fc::sha256::hash( egenesis_json );
386  return genesis;
387  }
388  };
389 
390  if( _options->count("resync-blockchain") )
391  _chain_db->wipe(_data_dir / "blockchain", true);
392 
393  flat_map<uint32_t,block_id_type> loaded_checkpoints;
394  if( _options->count("checkpoint") )
395  {
396  auto cps = _options->at("checkpoint").as<vector<string>>();
397  loaded_checkpoints.reserve( cps.size() );
398  for( auto cp : cps )
399  {
400  auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >( 2 );
401  loaded_checkpoints[item.first] = item.second;
402  }
403  }
404  _chain_db->add_checkpoints( loaded_checkpoints );
405 
406  if( _options->count("enable-standby-votes-tracking") )
407  {
408  _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as<bool>() );
409  }
410 
411  if( _options->count("replay-blockchain") || _options->count("revalidate-blockchain") )
412  _chain_db->wipe( _data_dir / "blockchain", false );
413 
414  try
415  {
416  // these flags are used in open() only, i. e. during replay
417  uint32_t skip;
418  if( _options->count("revalidate-blockchain") ) // see also handle_block()
419  {
420  if( !loaded_checkpoints.empty() )
421  wlog( "Warning - revalidate will not validate before last checkpoint" );
422  if( _options->count("force-validate") )
424  else
426  }
427  else // no revalidate, skip most checks
435 
436  graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () {
437  _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION );
438  });
439  }
440  catch( const fc::exception& e )
441  {
442  elog( "Caught exception ${e} in open(), you might want to force a replay", ("e", e.to_detail_string()) );
443  throw;
444  }
445 
446  if( _options->count("force-validate") )
447  {
448  ilog( "All transaction signatures will be validated" );
449  _force_validate = true;
450  }
451 
452  if ( _options->count("enable-subscribe-to-all") )
453  _app_options.enable_subscribe_to_all = _options->at( "enable-subscribe-to-all" ).as<bool>();
454 
455  set_api_limit();
456 
457  if( _active_plugins.find( "market_history" ) != _active_plugins.end() )
458  _app_options.has_market_history_plugin = true;
459 
460  if( _active_plugins.find( "api_helper_indexes" ) != _active_plugins.end() )
461  _app_options.has_api_helper_indexes_plugin = true;
462 
463  if( _options->count("api-access") ) {
464 
465  fc::path api_access_file = _options->at("api-access").as<boost::filesystem::path>();
466 
467  FC_ASSERT( fc::exists(api_access_file),
468  "Failed to load file from ${path}", ("path", api_access_file) );
469 
470  _apiaccess = fc::json::from_file( api_access_file ).as<api_access>( 20 );
471  ilog( "Using api access file from ${path}",
472  ("path", api_access_file) );
473  }
474  else
475  {
476  // TODO: Remove this generous default access policy
477  // when the UI logs in properly
478  _apiaccess = api_access();
479  api_access_info wild_access;
480  wild_access.password_hash_b64 = "*";
481  wild_access.password_salt_b64 = "*";
482  wild_access.allowed_apis.push_back( "database_api" );
483  wild_access.allowed_apis.push_back( "network_broadcast_api" );
484  wild_access.allowed_apis.push_back( "history_api" );
485  wild_access.allowed_apis.push_back( "orders_api" );
486  wild_access.allowed_apis.push_back( "custom_operations_api" );
487  _apiaccess.permission_map["*"] = wild_access;
488  }
489 
490  reset_p2p_node(_data_dir);
491  reset_websocket_server();
492  reset_websocket_tls_server();
493 } FC_LOG_AND_RETHROW() }
494 
496 {
498  auto it = _apiaccess.permission_map.find(username);
499  if( it == _apiaccess.permission_map.end() )
500  {
501  it = _apiaccess.permission_map.find("*");
502  if( it == _apiaccess.permission_map.end() )
503  return result;
504  }
505  return it->second;
506 }
507 
508 void application_impl::set_api_access_info(const string& username, api_access_info&& permissions)
509 {
510  _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions)));
511 }
512 
517 {
518  try
519  {
520  if( id.item_type == graphene::net::block_message_type )
521  return _chain_db->is_known_block(id.item_hash);
522  else
523  return _chain_db->is_known_transaction(id.item_hash);
524  }
525  FC_CAPTURE_AND_RETHROW( (id) )
526 }
527 
537  std::vector<fc::uint160_t>& contained_transaction_message_ids)
538 { try {
539 
540  auto latency = fc::time_point::now() - blk_msg.block.timestamp;
541  if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
542  {
543  const auto& witness = blk_msg.block.witness(*_chain_db);
544  const auto& witness_account = witness.witness_account(*_chain_db);
545  auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;
546  ilog("Got block: #${n} ${bid} time: ${t} transaction(s): ${x} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
547  ("t",blk_msg.block.timestamp)
548  ("n", blk_msg.block.block_num())
549  ("bid", blk_msg.block.id())
550  ("x", blk_msg.block.transactions.size())
551  ("l", (latency.count()/1000))
552  ("w",witness_account.name)
553  ("i",last_irr)("d",blk_msg.block.block_num()-last_irr) );
554  }
555  GRAPHENE_ASSERT( latency.count()/1000 > -5000,
556  graphene::net::block_timestamp_in_future_exception,
557  "Rejecting block with timestamp in the future", );
558 
559  try {
560  const uint32_t skip = (_is_block_producer | _force_validate) ?
562  bool result = valve.do_serial( [this,&blk_msg,skip] () {
563  _chain_db->precompute_parallel( blk_msg.block, skip ).wait();
564  }, [this,&blk_msg,skip] () {
565  // TODO: in the case where this block is valid but on a fork that's too old for us to switch to,
566  // you can help the network code out by throwing a block_older_than_undo_history exception.
567  // when the net code sees that, it will stop trying to push blocks from that chain, but
568  // leave that peer connected so that they can get sync blocks from us
569  return _chain_db->push_block( blk_msg.block, skip );
570  });
571 
572  // the block was accepted, so we now know all of the transactions contained in the block
573  if (!sync_mode)
574  {
575  // if we're not in sync mode, there's a chance we will be seeing some transactions
576  // included in blocks before we see the free-floating transaction itself. If that
577  // happens, there's no reason to fetch the transactions, so construct a list of the
578  // transaction message ids we no longer need.
579  // during sync, it is unlikely that we'll see any old
580  contained_transaction_message_ids.reserve( contained_transaction_message_ids.size()
581  + blk_msg.block.transactions.size() );
582  for (const processed_transaction& transaction : blk_msg.block.transactions)
583  {
584  graphene::net::trx_message transaction_message(transaction);
585  contained_transaction_message_ids.emplace_back(graphene::net::message(transaction_message).id());
586  }
587  }
588 
589  return result;
590  } catch ( const graphene::chain::unlinkable_block_exception& e ) {
591  // translate to a graphene::net exception
592  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
593  FC_THROW_EXCEPTION( graphene::net::unlinkable_block_exception,
594  "Error when pushing block:\n${e}",
595  ("e", e.to_detail_string()) );
596  } catch( const fc::exception& e ) {
597  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
598  throw;
599  }
600 
601  if( !_is_finished_syncing && !sync_mode )
602  {
603  _is_finished_syncing = true;
604  _self->syncing_finished();
605  }
606 } FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) return false; }
607 
609 { try {
610  static fc::time_point last_call;
611  static int trx_count = 0;
612  ++trx_count;
613  auto now = fc::time_point::now();
614  if( now - last_call > fc::seconds(1) ) {
615  ilog("Got ${c} transactions from network", ("c",trx_count) );
616  last_call = now;
617  trx_count = 0;
618  }
619 
620  _chain_db->precompute_parallel( transaction_message.trx ).wait();
621  _chain_db->push_transaction( transaction_message.trx );
622 } FC_CAPTURE_AND_RETHROW( (transaction_message) ) }
623 
624 void application_impl::handle_message(const message& message_to_process)
625 {
626  // not a transaction, not a block
627  FC_THROW( "Invalid Message Type" );
628 }
629 
631 {
632  uint32_t block_num = block_header::num_from_id(block_id);
633  block_id_type block_id_in_preferred_chain = _chain_db->get_block_id_for_num(block_num);
634  return block_id == block_id_in_preferred_chain;
635 }
636 
646 std::vector<item_hash_t> application_impl::get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
647  uint32_t& remaining_item_count,
648  uint32_t limit)
649 { try {
650  vector<block_id_type> result;
651  remaining_item_count = 0;
652  if( _chain_db->head_block_num() == 0 )
653  return result;
654 
655  result.reserve(limit);
656  block_id_type last_known_block_id;
657 
658  if (blockchain_synopsis.empty() ||
659  (blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
660  {
661  // peer has sent us an empty synopsis meaning they have no blocks.
662  // A bug in old versions would cause them to send a synopsis containing block 000000000
663  // when they had an empty blockchain, so pretend they sent the right thing here.
664 
665  // do nothing, leave last_known_block_id set to zero
666  }
667  else
668  {
669  bool found_a_block_in_synopsis = false;
670  for (const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis))
671  if (block_id_in_synopsis == block_id_type() ||
672  (_chain_db->is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis)))
673  {
674  last_known_block_id = block_id_in_synopsis;
675  found_a_block_in_synopsis = true;
676  break;
677  }
678  if (!found_a_block_in_synopsis)
679  FC_THROW_EXCEPTION( graphene::net::peer_is_on_an_unreachable_fork,
680  "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis" );
681  }
682  for( uint32_t num = block_header::num_from_id(last_known_block_id);
683  num <= _chain_db->head_block_num() && result.size() < limit;
684  ++num )
685  if( num > 0 )
686  result.push_back(_chain_db->get_block_id_for_num(num));
687 
688  if( !result.empty() && block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
689  remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
690 
691  return result;
692 } FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }
693 
697 message application_impl::get_item(const item_id& id)
698 { try {
699  // ilog("Request for item ${id}", ("id", id));
700  if( id.item_type == graphene::net::block_message_type )
701  {
702  auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
703  if( !opt_block )
704  elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}",
705  ("id", id.item_hash)("id2", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));
706  FC_ASSERT( opt_block.valid() );
707  // ilog("Serving up block #${num}", ("num", opt_block->block_num()));
708  return block_message(std::move(*opt_block));
709  }
710  return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
711 } FC_CAPTURE_AND_RETHROW( (id) ) }
712 
714 {
715  return _chain_db->get_chain_id();
716 }
717 
776 std::vector<item_hash_t> application_impl::get_blockchain_synopsis(const item_hash_t& reference_point,
777  uint32_t number_of_blocks_after_reference_point)
778 { try {
779  std::vector<item_hash_t> synopsis;
780  synopsis.reserve(30);
781  uint32_t high_block_num;
782  uint32_t non_fork_high_block_num;
783  uint32_t low_block_num = _chain_db->last_non_undoable_block_num();
784  std::vector<block_id_type> fork_history;
785 
786  if (reference_point != item_hash_t())
787  {
788  // the node is asking for a summary of the block chain up to a specified
789  // block, which may or may not be on a fork
790  // for now, assume it's not on a fork
791  if (is_included_block(reference_point))
792  {
793  // reference_point is a block we know about and is on the main chain
794  uint32_t reference_point_block_num = block_header::num_from_id(reference_point);
795  assert(reference_point_block_num > 0);
796  high_block_num = reference_point_block_num;
797  non_fork_high_block_num = high_block_num;
798 
799  if (reference_point_block_num < low_block_num)
800  {
801  // we're on the same fork (at least as far as reference_point) but we've passed
802  // reference point and could no longer undo that far if we diverged after that
803  // block. This should probably only happen due to a race condition where
804  // the network thread calls this function, and then immediately pushes a bunch of blocks,
805  // then the main thread finally processes this function.
806  // with the current framework, there's not much we can do to tell the network
807  // thread what our current head block is, so we'll just pretend that
808  // our head is actually the reference point.
809  // this *may* enable us to fetch blocks that we're unable to push, but that should
810  // be a rare case (and correctly handled)
811  low_block_num = reference_point_block_num;
812  }
813  }
814  else
815  {
816  // block is a block we know about, but it is on a fork
817  try
818  {
819  fork_history = _chain_db->get_block_ids_on_fork(reference_point);
820  // returns a vector where the last element is the common ancestor with the preferred chain,
821  // and the first element is the reference point you passed in
822  assert(fork_history.size() >= 2);
823 
824  if( fork_history.front() != reference_point )
825  {
826  edump( (fork_history)(reference_point) );
827  assert(fork_history.front() == reference_point);
828  }
829  block_id_type last_non_fork_block = fork_history.back();
830  fork_history.pop_back(); // remove the common ancestor
831  boost::reverse(fork_history);
832 
833  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?)
834  non_fork_high_block_num = 0;
835  else
836  non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);
837 
838  high_block_num = non_fork_high_block_num + fork_history.size();
839  assert(high_block_num == block_header::num_from_id(fork_history.back()));
840  }
841  catch (const fc::exception& e)
842  {
843  // unable to get fork history for some reason. maybe not linked?
844  // we can't return a synopsis of its chain
845  elog( "Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}",
846  ("hash", reference_point)("exception", e) );
847  throw;
848  }
849  if (non_fork_high_block_num < low_block_num)
850  {
851  wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
852  "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
853  ("low_block_num", low_block_num)
854  ("non_fork_high_block_num", non_fork_high_block_num));
855  FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
856  }
857  }
858  }
859  else
860  {
861  // no reference point specified, summarize the whole block chain
862  high_block_num = _chain_db->head_block_num();
863  non_fork_high_block_num = high_block_num;
864  if (high_block_num == 0)
865  return synopsis; // we have no blocks
866  }
867 
868  if( low_block_num == 0)
869  low_block_num = 1;
870 
871  // at this point:
872  // low_block_num is the block before the first block we can undo,
873  // 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)
874  // high_block_num is the block number of the reference block, or the end of the chain if no reference provided
875 
876  // true_high_block_num is the ending block number after the network code appends any item ids it
877  // knows about that we don't
878  uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
879  do
880  {
881  // for each block in the synopsis, figure out where to pull the block id from.
882  // if it's <= non_fork_high_block_num, we grab it from the main blockchain;
883  // if it's not, we pull it from the fork history
884  if (low_block_num <= non_fork_high_block_num)
885  synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));
886  else
887  synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);
888  low_block_num += (true_high_block_num - low_block_num + 2) / 2;
889  }
890  while (low_block_num <= high_block_num);
891 
892  //idump((synopsis));
893  return synopsis;
895 
903 void application_impl::sync_status(uint32_t item_type, uint32_t item_count)
904 {
905  // any status reports to GUI go here
906 }
907 
912 {
913  // any status reports to GUI go here
914 }
915 
917 { try {
918  return block_header::num_from_id(block_id);
919 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
920 
926 { try {
927  auto opt_block = _chain_db->fetch_block_by_id( block_id );
928  if( opt_block.valid() ) return opt_block->timestamp;
929  return fc::time_point_sec::min();
930 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
931 
933 {
934  return _chain_db->head_block_id();
935 }
936 
938 {
939  return 0; // there are no forks in graphene
940 }
941 
942 void application_impl::error_encountered(const std::string& message, const fc::oexception& error)
943 {
944  // notify GUI or something cool
945 }
946 
948 {
949  return _chain_db->get_global_properties().parameters.block_interval;
950 }
951 
952 
953 
954 } } } // namespace graphene namespace app namespace detail
955 
956 namespace graphene { namespace app {
957 
959  : my(new detail::application_impl(this))
960 {}
961 
963 {
964  if( my->_p2p_network )
965  {
966  my->_p2p_network->close();
967  my->_p2p_network.reset();
968  }
969  if( my->_chain_db )
970  {
971  my->_chain_db->close();
972  }
973 }
974 
975 void application::set_program_options(boost::program_options::options_description& command_line_options,
976  boost::program_options::options_description& configuration_file_options) const
977 {
978  configuration_file_options.add_options()
979  ("p2p-endpoint", bpo::value<string>(), "Endpoint for P2P node to listen on")
980  ("seed-node,s", bpo::value<vector<string>>()->composing(),
981  "P2P nodes to connect to on startup (may specify multiple times)")
982  ("seed-nodes", bpo::value<string>()->composing(),
983  "JSON array of P2P nodes to connect to on startup")
984  ("checkpoint,c", bpo::value<vector<string>>()->composing(),
985  "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
986  ("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"),
987  "Endpoint for websocket RPC to listen on")
988  ("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"),
989  "Endpoint for TLS websocket RPC to listen on")
990  ("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
991  ("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
992  ("proxy-forwarded-for-header", bpo::value<string>()->implicit_value("X-Forwarded-For-Client"),
993  "A HTTP header similar to X-Forwarded-For (XFF), used by the RPC server to extract clients' address info, "
994  "usually added by a trusted reverse proxy")
995  ("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
996  ("dbg-init-key", bpo::value<string>(), "Block signing key to use for init witnesses, overrides genesis file")
997  ("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
998  ("io-threads", bpo::value<uint16_t>()->implicit_value(0), "Number of IO threads, default to 0 for auto-configuration")
999  ("enable-subscribe-to-all", bpo::value<bool>()->implicit_value(true),
1000  "Whether allow API clients to subscribe to universal object creation and removal events")
1001  ("enable-standby-votes-tracking", bpo::value<bool>()->implicit_value(true),
1002  "Whether to enable tracking of votes of standby witnesses and committee members. "
1003  "Set it to true to provide accurate data to API clients, set to false for slightly better performance.")
1004  ("api-limit-get-account-history-operations",boost::program_options::value<uint64_t>()->default_value(100),
1005  "For history_api::get_account_history_operations to set max limit value")
1006  ("api-limit-get-account-history",boost::program_options::value<uint64_t>()->default_value(100),
1007  "For history_api::get_account_history to set max limit value")
1008  ("api-limit-get-grouped-limit-orders",boost::program_options::value<uint64_t>()->default_value(101),
1009  "For orders_api::get_grouped_limit_orders to set max limit value")
1010  ("api-limit-get-relative-account-history",boost::program_options::value<uint64_t>()->default_value(100),
1011  "For history_api::get_relative_account_history to set max limit value")
1012  ("api-limit-get-account-history-by-operations",boost::program_options::value<uint64_t>()->default_value(100),
1013  "For history_api::get_account_history_by_operations to set max limit value")
1014  ("api-limit-get-asset-holders",boost::program_options::value<uint64_t>()->default_value(100),
1015  "For asset_api::get_asset_holders to set max limit value")
1016  ("api-limit-get-key-references",boost::program_options::value<uint64_t>()->default_value(100),
1017  "For database_api_impl::get_key_references to set max limit value")
1018  ("api-limit-get-htlc-by",boost::program_options::value<uint64_t>()->default_value(100),
1019  "For database_api_impl::get_htlc_by_from and get_htlc_by_to to set max limit value")
1020  ("api-limit-get-full-accounts",boost::program_options::value<uint64_t>()->default_value(50),
1021  "For database_api_impl::get_full_accounts to set max accounts to query at once")
1022  ("api-limit-get-full-accounts-lists",boost::program_options::value<uint64_t>()->default_value(500),
1023  "For database_api_impl::get_full_accounts to set max items to return in the lists")
1024  ("api-limit-get-top-voters",boost::program_options::value<uint64_t>()->default_value(200),
1025  "For database_api_impl::get_top_voters to set max limit value")
1026  ("api-limit-get-call-orders",boost::program_options::value<uint64_t>()->default_value(300),
1027  "For database_api_impl::get_call_orders and get_call_orders_by_account to set max limit value")
1028  ("api-limit-get-settle-orders",boost::program_options::value<uint64_t>()->default_value(300),
1029  "For database_api_impl::get_settle_orders and get_settle_orders_by_account to set max limit value")
1030  ("api-limit-get-assets",boost::program_options::value<uint64_t>()->default_value(101),
1031  "For database_api_impl::list_assets and get_assets_by_issuer to set max limit value")
1032  ("api-limit-get-limit-orders",boost::program_options::value<uint64_t>()->default_value(300),
1033  "For database_api_impl::get_limit_orders to set max limit value")
1034  ("api-limit-get-limit-orders-by-account",boost::program_options::value<uint64_t>()->default_value(101),
1035  "For database_api_impl::get_limit_orders_by_account to set max limit value")
1036  ("api-limit-get-order-book",boost::program_options::value<uint64_t>()->default_value(50),
1037  "For database_api_impl::get_order_book to set max limit value")
1038  ("api-limit-lookup-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1039  "For database_api_impl::lookup_accounts to set max limit value")
1040  ("api-limit-lookup-witness-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1041  "For database_api_impl::lookup_witness_accounts to set max limit value")
1042  ("api-limit-lookup-committee-member-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1043  "For database_api_impl::lookup_committee_member_accounts to set max limit value")
1044  ("api-limit-lookup-vote-ids",boost::program_options::value<uint64_t>()->default_value(1000),
1045  "For database_api_impl::lookup_vote_ids to set max limit value")
1046  ("api-limit-get-account-limit-orders",boost::program_options::value<uint64_t>()->default_value(101),
1047  "For database_api_impl::get_account_limit_orders to set max limit value")
1048  ("api-limit-get-collateral-bids",boost::program_options::value<uint64_t>()->default_value(100),
1049  "For database_api_impl::get_collateral_bids to set max limit value")
1050  ("api-limit-get-top-markets",boost::program_options::value<uint64_t>()->default_value(100),
1051  "For database_api_impl::get_top_markets to set max limit value")
1052  ("api-limit-get-trade-history",boost::program_options::value<uint64_t>()->default_value(100),
1053  "For database_api_impl::get_trade_history to set max limit value")
1054  ("api-limit-get-trade-history-by-sequence",boost::program_options::value<uint64_t>()->default_value(100),
1055  "For database_api_impl::get_trade_history_by_sequence to set max limit value")
1056  ("api-limit-get-withdraw-permissions-by-giver",boost::program_options::value<uint64_t>()->default_value(101),
1057  "For database_api_impl::get_withdraw_permissions_by_giver to set max limit value")
1058  ("api-limit-get-withdraw-permissions-by-recipient",boost::program_options::value<uint64_t>()->default_value(101),
1059  "For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value")
1060  ("api-limit-get-liquidity-pools",boost::program_options::value<uint64_t>()->default_value(101),
1061  "For database_api_impl::get_liquidity_pools_* to set max limit value")
1062  ;
1063  command_line_options.add(configuration_file_options);
1064  command_line_options.add_options()
1065  ("replay-blockchain", "Rebuild object graph by replaying all blocks without validation")
1066  ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation")
1067  ("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
1068  ("force-validate", "Force validation of all transactions during normal operation")
1069  ("genesis-timestamp", bpo::value<uint32_t>(),
1070  "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)")
1071  ;
1072  command_line_options.add(_cli_options);
1073  configuration_file_options.add(_cfg_options);
1074 }
1075 
1076 void application::initialize(const fc::path& data_dir, const boost::program_options::variables_map& options)
1077 {
1078  my->_data_dir = data_dir;
1079  my->_options = &options;
1080 
1081  if ( options.count("io-threads") )
1082  {
1083  const uint16_t num_threads = options["io-threads"].as<uint16_t>();
1085  }
1086 }
1087 
1089 {
1090  try {
1091  my->startup();
1092  } catch ( const fc::exception& e ) {
1093  elog( "${e}", ("e",e.to_detail_string()) );
1094  throw;
1095  } catch ( ... ) {
1096  elog( "unexpected exception" );
1097  throw;
1098  }
1099 }
1100 
1102 {
1103  try {
1104  my->set_api_limit();
1105  } catch ( const fc::exception& e ) {
1106  elog( "${e}", ("e",e.to_detail_string()) );
1107  throw;
1108  } catch ( ... ) {
1109  elog( "unexpected exception" );
1110  throw;
1111  }
1112 }
1113 std::shared_ptr<abstract_plugin> application::get_plugin(const string& name) const
1114 {
1115  return my->_active_plugins[name];
1116 }
1117 
1118 bool application::is_plugin_enabled(const string& name) const
1119 {
1120  return !(my->_active_plugins.find(name) == my->_active_plugins.end());
1121 }
1122 
1124 {
1125  return my->_p2p_network;
1126 }
1127 
1128 std::shared_ptr<chain::database> application::chain_database() const
1129 {
1130  return my->_chain_db;
1131 }
1132 
1133 void application::set_block_production(bool producing_blocks)
1134 {
1135  my->_is_block_producer = producing_blocks;
1136 }
1137 
1139 {
1140  return my->get_api_access_info( username );
1141 }
1142 
1143 void application::set_api_access_info(const string& username, api_access_info&& permissions)
1144 {
1145  my->set_api_access_info(username, std::move(permissions));
1146 }
1147 
1149 {
1150  return my->_is_finished_syncing;
1151 }
1152 
1154 {
1155  FC_ASSERT(my->_available_plugins[name], "Unknown plugin '" + name + "'");
1156  my->_active_plugins[name] = my->_available_plugins[name];
1157  my->_active_plugins[name]->plugin_set_app(this);
1158 }
1159 
1160 void graphene::app::application::add_available_plugin(std::shared_ptr<graphene::app::abstract_plugin> p)
1161 {
1162  my->_available_plugins[p->plugin_name()] = p;
1163 }
1164 
1166 {
1167  for( auto& entry : my->_active_plugins )
1168  entry.second->plugin_shutdown();
1169  return;
1170 }
1172 {
1173  if( my->_p2p_network )
1174  my->_p2p_network->close();
1175  if( my->_chain_db )
1176  {
1177  my->_chain_db->close();
1178  my->_chain_db = nullptr;
1179  }
1180 }
1181 
1182 void application::initialize_plugins( const boost::program_options::variables_map& options )
1183 {
1184  for( auto& entry : my->_active_plugins )
1185  entry.second->plugin_initialize( options );
1186  return;
1187 }
1188 
1190 {
1191  for( auto& entry : my->_active_plugins )
1192  {
1193  entry.second->plugin_startup();
1194  ilog( "Plugin ${name} started", ( "name", entry.second->plugin_name() ) );
1195  }
1196  return;
1197 }
1198 
1200 {
1201  return my->_app_options;
1202 }
1203 
1204 // namespace detail
1205 } }
virtual 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
bool exists(const path &p)
Definition: filesystem.cpp:209
fc::optional< api_access_info > get_api_access_info(const string &username) const
fc::time_point_sec timestamp
Definition: block.hpp:35
virtual bool handle_block(const graphene::net::block_message &blk_msg, bool sync_mode, std::vector< fc::uint160_t > &contained_transaction_message_ids) override
allows the application to validate an item prior to broadcasting to peers.
fc::optional< api_access_info > get_api_access_info(const string &username) const
#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT
Definition: config.hpp:77
T as(uint32_t max_depth) const
Definition: variant.hpp:336
virtual bool has_item(const net::item_id &id) override
static uint32_t num_from_id(const block_id_type &id)
Definition: block.cpp:36
graphene::chain::genesis_state_type create_example_genesis()
Definition: application.cpp:79
uint32_t sec_since_epoch() const
Definition: time.hpp:90
std::shared_ptr< node > node_ptr
Definition: node.hpp:345
#define GRAPHENE_NET_MAX_NESTED_OBJECTS
Definition: config.hpp:110
void set_dbg_init_key(graphene::chain::genesis_state_type &genesis, const std::string &init_key)
virtual 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)
void set_program_options(boost::program_options::options_description &command_line_options, boost::program_options::options_description &configuration_file_options) const
virtual uint32_t get_block_number(const graphene::net::item_hash_t &block_id) override
virtual 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::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
static fee_schedule get_default()
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
#define FC_THROW(...)
Definition: exception.hpp:366
groups operations that should be applied atomically
Definition: transaction.hpp:68
void read_file_contents(const fc::path &filename, std::string &result)
Definition: fstream.cpp:107
vector< initial_witness_type > initial_witness_candidates
bool is_plugin_enabled(const string &name) const
#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:755
std::shared_ptr< websocket_connection > websocket_connection_ptr
Definition: websocket.hpp:47
#define GRAPHENE_MAX_SHARE_SUPPLY
Definition: config.hpp:38
fc::ripemd160 item_hash_t
fc::ripemd160 block_id_type
Definition: types.hpp:242
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)
vector< initial_account_type > initial_accounts
void set_api_access_info(const string &username, api_access_info &&permissions)
std::shared_ptr< abstract_plugin > get_plugin(const string &name) const
void set_block_production(bool producing_blocks)
#define edump(SEQ)
Definition: logger.hpp:182
microseconds seconds(int64_t s)
Definition: time.hpp:34
bool is_finished_syncing() const
#define GRAPHENE_CURRENT_DB_VERSION
Definition: config.hpp:33
#define ilog(FORMAT,...)
Definition: logger.hpp:117
virtual graphene::net::item_hash_t get_head_block_id() const override
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
virtual 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)
void initialize(const fc::path &data_dir, const boost::program_options::variables_map &options)
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:378
static endpoint from_string(const string &s)
Definition: ip.cpp:74
virtual 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)
virtual fc::time_point_sec get_block_time(const graphene::net::item_hash_t &block_id) override
void initialize_plugins(const boost::program_options::variables_map &options)
used while reindexing – note this skips expiration check as well
Definition: database.hpp:85
vector< initial_committee_member_type > initial_committee_candidates
#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()
std::string to_iso_string() const
Definition: time.cpp:24
virtual 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
uint32_t block_num() const
Definition: block.hpp:34
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
const block_id_type & id() const
Definition: block.cpp:41
virtual 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
virtual graphene::net::message get_item(const graphene::net::item_id &id) override
std::shared_ptr< chain::database > chain_database() const
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:394
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
virtual graphene::chain::chain_id_type get_chain_id() const override
void enable_plugin(const string &name)
uint8_t block_interval
interval in seconds between blocks
static void set_num_threads(uint16_t num_threads)
Definition: asio.cpp:103
used when applying locally generated transactions
Definition: database.hpp:84