BitShares-Core  4.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  _websocket_server = std::make_shared<fc::http::websocket_server>();
196  _websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
197 
198  ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
199  _websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
200  _websocket_server->start_accept();
202 
204 { try {
205  if( !_options->count("rpc-tls-endpoint") )
206  return;
207  if( !_options->count("server-pem") )
208  {
209  wlog( "Please specify a server-pem to use rpc-tls-endpoint" );
210  return;
211  }
212 
213  string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
214  _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password );
215  _websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
216 
217  ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
218  _websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
219  _websocket_tls_server->start_accept();
221 
222 void application_impl::set_dbg_init_key( graphene::chain::genesis_state_type& genesis, const std::string& init_key )
223 {
224  flat_set< std::string > initial_witness_names;
225  public_key_type init_pubkey( init_key );
226  for( uint64_t i=0; i<genesis.initial_active_witnesses; i++ )
227  genesis.initial_witness_candidates[i].block_signing_key = init_pubkey;
228 }
229 
230 
231 
233  if (_options->count("api-limit-get-account-history-operations")) {
234  _app_options.api_limit_get_account_history_operations = _options->at("api-limit-get-account-history-operations").as<uint64_t>();
235  }
236  if(_options->count("api-limit-get-account-history")){
237  _app_options.api_limit_get_account_history = _options->at("api-limit-get-account-history").as<uint64_t>();
238  }
239  if(_options->count("api-limit-get-grouped-limit-orders")){
240  _app_options.api_limit_get_grouped_limit_orders = _options->at("api-limit-get-grouped-limit-orders").as<uint64_t>();
241  }
242  if(_options->count("api-limit-get-relative-account-history")){
243  _app_options.api_limit_get_relative_account_history = _options->at("api-limit-get-relative-account-history").as<uint64_t>();
244  }
245  if(_options->count("api-limit-get-account-history-by-operations")){
246  _app_options.api_limit_get_account_history_by_operations = _options->at("api-limit-get-account-history-by-operations").as<uint64_t>();
247  }
248  if(_options->count("api-limit-get-asset-holders")){
249  _app_options.api_limit_get_asset_holders = _options->at("api-limit-get-asset-holders").as<uint64_t>();
250  }
251  if(_options->count("api-limit-get-key-references")){
252  _app_options.api_limit_get_key_references = _options->at("api-limit-get-key-references").as<uint64_t>();
253  }
254  if(_options->count("api-limit-get-htlc-by")) {
255  _app_options.api_limit_get_htlc_by = _options->at("api-limit-get-htlc-by").as<uint64_t>();
256  }
257  if(_options->count("api-limit-get-full-accounts")) {
258  _app_options.api_limit_get_full_accounts = _options->at("api-limit-get-full-accounts").as<uint64_t>();
259  }
260  if(_options->count("api-limit-get-full-accounts-lists")) {
261  _app_options.api_limit_get_full_accounts_lists = _options->at("api-limit-get-full-accounts-lists").as<uint64_t>();
262  }
263  if(_options->count("api-limit-get-call-orders")) {
264  _app_options.api_limit_get_call_orders = _options->at("api-limit-get-call-orders").as<uint64_t>();
265  }
266  if(_options->count("api-limit-get-settle-orders")) {
267  _app_options.api_limit_get_settle_orders = _options->at("api-limit-get-settle-orders").as<uint64_t>();
268  }
269  if(_options->count("api-limit-get-assets")) {
270  _app_options.api_limit_get_assets = _options->at("api-limit-get-assets").as<uint64_t>();
271  }
272  if(_options->count("api-limit-get-limit-orders")){
273  _app_options.api_limit_get_limit_orders = _options->at("api-limit-get-limit-orders").as<uint64_t>();
274  }
275  if(_options->count("api-limit-get-limit-orders-by-account")){
276  _app_options.api_limit_get_limit_orders_by_account = _options->at("api-limit-get-limit-orders-by-account").as<uint64_t>();
277  }
278  if(_options->count("api-limit-get-order-book")){
279  _app_options.api_limit_get_order_book = _options->at("api-limit-get-order-book").as<uint64_t>();
280  }
281  if(_options->count("api-limit-list-htlcs")){
282  _app_options.api_limit_list_htlcs = _options->at("api-limit-list-htlcs").as<uint64_t>();
283  }
284  if(_options->count("api-limit-lookup-accounts")) {
285  _app_options.api_limit_lookup_accounts = _options->at("api-limit-lookup-accounts").as<uint64_t>();
286  }
287  if(_options->count("api-limit-lookup-witness-accounts")) {
288  _app_options.api_limit_lookup_witness_accounts = _options->at("api-limit-lookup-witness-accounts").as<uint64_t>();
289  }
290  if(_options->count("api-limit-lookup-committee-member-accounts")) {
291  _app_options.api_limit_lookup_committee_member_accounts = _options->at("api-limit-lookup-committee-member-accounts").as<uint64_t>();
292  }
293  if(_options->count("api-limit-lookup-vote-ids")) {
294  _app_options.api_limit_lookup_vote_ids = _options->at("api-limit-lookup-vote-ids").as<uint64_t>();
295  }
296  if(_options->count("api-limit-get-account-limit-orders")) {
297  _app_options.api_limit_get_account_limit_orders = _options->at("api-limit-get-account-limit-orders").as<uint64_t>();
298  }
299  if(_options->count("api-limit-get-collateral-bids")) {
300  _app_options.api_limit_get_collateral_bids = _options->at("api-limit-get-collateral-bids").as<uint64_t>();
301  }
302  if(_options->count("api-limit-get-top-markets")) {
303  _app_options.api_limit_get_top_markets = _options->at("api-limit-get-top-markets").as<uint64_t>();
304  }
305  if(_options->count("api-limit-get-trade-history")) {
306  _app_options.api_limit_get_trade_history = _options->at("api-limit-get-trade-history").as<uint64_t>();
307  }
308  if(_options->count("api-limit-get-trade-history-by-sequence")) {
309  _app_options.api_limit_get_trade_history_by_sequence = _options->at("api-limit-get-trade-history-by-sequence").as<uint64_t>();
310  }
311  if(_options->count("api-limit-get-withdraw-permissions-by-giver")) {
312  _app_options.api_limit_get_withdraw_permissions_by_giver = _options->at("api-limit-get-withdraw-permissions-by-giver").as<uint64_t>();
313  }
314  if(_options->count("api-limit-get-withdraw-permissions-by-recipient")) {
315  _app_options.api_limit_get_withdraw_permissions_by_recipient = _options->at("api-limit-get-withdraw-permissions-by-recipient").as<uint64_t>();
316  }
317 }
318 
320 { try {
321  fc::create_directories(_data_dir / "blockchain");
322 
323  auto initial_state = [this] {
324  ilog("Initializing database...");
325  if( _options->count("genesis-json") )
326  {
327  std::string genesis_str;
328  fc::read_file_contents( _options->at("genesis-json").as<boost::filesystem::path>(), genesis_str );
330  bool modified_genesis = false;
331  if( _options->count("genesis-timestamp") )
332  {
335  + _options->at("genesis-timestamp").as<uint32_t>();
338  modified_genesis = true;
339 
340  ilog(
341  "Used genesis timestamp: ${timestamp} (PLEASE RECORD THIS)",
342  ("timestamp", genesis.initial_timestamp.to_iso_string())
343  );
344  }
345  if( _options->count("dbg-init-key") )
346  {
347  std::string init_key = _options->at( "dbg-init-key" ).as<string>();
349  set_dbg_init_key( genesis, init_key );
350  modified_genesis = true;
351  ilog("Set init witness key to ${init_key}", ("init_key", init_key));
352  }
353  if( modified_genesis )
354  {
355  wlog("WARNING: GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT");
356  genesis_str += "BOGUS";
357  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
358  }
359  else
360  genesis.initial_chain_id = fc::sha256::hash( genesis_str );
361  return genesis;
362  }
363  else
364  {
365  std::string egenesis_json;
367  FC_ASSERT( egenesis_json != "" );
369  auto genesis = fc::json::from_string( egenesis_json ).as<graphene::chain::genesis_state_type>( 20 );
370  genesis.initial_chain_id = fc::sha256::hash( egenesis_json );
371  return genesis;
372  }
373  };
374 
375  if( _options->count("resync-blockchain") )
376  _chain_db->wipe(_data_dir / "blockchain", true);
377 
378  flat_map<uint32_t,block_id_type> loaded_checkpoints;
379  if( _options->count("checkpoint") )
380  {
381  auto cps = _options->at("checkpoint").as<vector<string>>();
382  loaded_checkpoints.reserve( cps.size() );
383  for( auto cp : cps )
384  {
385  auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >( 2 );
386  loaded_checkpoints[item.first] = item.second;
387  }
388  }
389  _chain_db->add_checkpoints( loaded_checkpoints );
390 
391  if( _options->count("enable-standby-votes-tracking") )
392  {
393  _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as<bool>() );
394  }
395 
396  if( _options->count("replay-blockchain") || _options->count("revalidate-blockchain") )
397  _chain_db->wipe( _data_dir / "blockchain", false );
398 
399  try
400  {
401  // these flags are used in open() only, i. e. during replay
402  uint32_t skip;
403  if( _options->count("revalidate-blockchain") ) // see also handle_block()
404  {
405  if( !loaded_checkpoints.empty() )
406  wlog( "Warning - revalidate will not validate before last checkpoint" );
407  if( _options->count("force-validate") )
409  else
411  }
412  else // no revalidate, skip most checks
420 
421  graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () {
422  _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION );
423  });
424  }
425  catch( const fc::exception& e )
426  {
427  elog( "Caught exception ${e} in open(), you might want to force a replay", ("e", e.to_detail_string()) );
428  throw;
429  }
430 
431  if( _options->count("force-validate") )
432  {
433  ilog( "All transaction signatures will be validated" );
434  _force_validate = true;
435  }
436 
437  if ( _options->count("enable-subscribe-to-all") )
438  _app_options.enable_subscribe_to_all = _options->at( "enable-subscribe-to-all" ).as<bool>();
439 
440  set_api_limit();
441 
442  if( _active_plugins.find( "market_history" ) != _active_plugins.end() )
443  _app_options.has_market_history_plugin = true;
444 
445  if( _active_plugins.find( "api_helper_indexes" ) != _active_plugins.end() )
446  _app_options.has_api_helper_indexes_plugin = true;
447 
448  if( _options->count("api-access") ) {
449 
450  fc::path api_access_file = _options->at("api-access").as<boost::filesystem::path>();
451 
452  FC_ASSERT( fc::exists(api_access_file),
453  "Failed to load file from ${path}", ("path", api_access_file) );
454 
455  _apiaccess = fc::json::from_file( api_access_file ).as<api_access>( 20 );
456  ilog( "Using api access file from ${path}",
457  ("path", api_access_file) );
458  }
459  else
460  {
461  // TODO: Remove this generous default access policy
462  // when the UI logs in properly
463  _apiaccess = api_access();
464  api_access_info wild_access;
465  wild_access.password_hash_b64 = "*";
466  wild_access.password_salt_b64 = "*";
467  wild_access.allowed_apis.push_back( "database_api" );
468  wild_access.allowed_apis.push_back( "network_broadcast_api" );
469  wild_access.allowed_apis.push_back( "history_api" );
470  wild_access.allowed_apis.push_back( "orders_api" );
471  wild_access.allowed_apis.push_back( "custom_operations_api" );
472  _apiaccess.permission_map["*"] = wild_access;
473  }
474 
475  reset_p2p_node(_data_dir);
476  reset_websocket_server();
477  reset_websocket_tls_server();
478 } FC_LOG_AND_RETHROW() }
479 
481 {
483  auto it = _apiaccess.permission_map.find(username);
484  if( it == _apiaccess.permission_map.end() )
485  {
486  it = _apiaccess.permission_map.find("*");
487  if( it == _apiaccess.permission_map.end() )
488  return result;
489  }
490  return it->second;
491 }
492 
493 void application_impl::set_api_access_info(const string& username, api_access_info&& permissions)
494 {
495  _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions)));
496 }
497 
502 {
503  try
504  {
505  if( id.item_type == graphene::net::block_message_type )
506  return _chain_db->is_known_block(id.item_hash);
507  else
508  return _chain_db->is_known_transaction(id.item_hash);
509  }
510  FC_CAPTURE_AND_RETHROW( (id) )
511 }
512 
522  std::vector<fc::uint160_t>& contained_transaction_message_ids)
523 { try {
524 
525  auto latency = fc::time_point::now() - blk_msg.block.timestamp;
526  if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
527  {
528  const auto& witness = blk_msg.block.witness(*_chain_db);
529  const auto& witness_account = witness.witness_account(*_chain_db);
530  auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;
531  ilog("Got block: #${n} ${bid} time: ${t} transaction(s): ${x} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
532  ("t",blk_msg.block.timestamp)
533  ("n", blk_msg.block.block_num())
534  ("bid", blk_msg.block.id())
535  ("x", blk_msg.block.transactions.size())
536  ("l", (latency.count()/1000))
537  ("w",witness_account.name)
538  ("i",last_irr)("d",blk_msg.block.block_num()-last_irr) );
539  }
540  GRAPHENE_ASSERT( latency.count()/1000 > -5000,
541  graphene::net::block_timestamp_in_future_exception,
542  "Rejecting block with timestamp in the future", );
543 
544  try {
545  const uint32_t skip = (_is_block_producer | _force_validate) ?
547  bool result = valve.do_serial( [this,&blk_msg,skip] () {
548  _chain_db->precompute_parallel( blk_msg.block, skip ).wait();
549  }, [this,&blk_msg,skip] () {
550  // TODO: in the case where this block is valid but on a fork that's too old for us to switch to,
551  // you can help the network code out by throwing a block_older_than_undo_history exception.
552  // when the net code sees that, it will stop trying to push blocks from that chain, but
553  // leave that peer connected so that they can get sync blocks from us
554  return _chain_db->push_block( blk_msg.block, skip );
555  });
556 
557  // the block was accepted, so we now know all of the transactions contained in the block
558  if (!sync_mode)
559  {
560  // if we're not in sync mode, there's a chance we will be seeing some transactions
561  // included in blocks before we see the free-floating transaction itself. If that
562  // happens, there's no reason to fetch the transactions, so construct a list of the
563  // transaction message ids we no longer need.
564  // during sync, it is unlikely that we'll see any old
565  contained_transaction_message_ids.reserve( contained_transaction_message_ids.size()
566  + blk_msg.block.transactions.size() );
567  for (const processed_transaction& transaction : blk_msg.block.transactions)
568  {
569  graphene::net::trx_message transaction_message(transaction);
570  contained_transaction_message_ids.emplace_back(graphene::net::message(transaction_message).id());
571  }
572  }
573 
574  return result;
575  } catch ( const graphene::chain::unlinkable_block_exception& e ) {
576  // translate to a graphene::net exception
577  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
578  FC_THROW_EXCEPTION( graphene::net::unlinkable_block_exception,
579  "Error when pushing block:\n${e}",
580  ("e", e.to_detail_string()) );
581  } catch( const fc::exception& e ) {
582  elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
583  throw;
584  }
585 
586  if( !_is_finished_syncing && !sync_mode )
587  {
588  _is_finished_syncing = true;
589  _self->syncing_finished();
590  }
591 } FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) return false; }
592 
594 { try {
595  static fc::time_point last_call;
596  static int trx_count = 0;
597  ++trx_count;
598  auto now = fc::time_point::now();
599  if( now - last_call > fc::seconds(1) ) {
600  ilog("Got ${c} transactions from network", ("c",trx_count) );
601  last_call = now;
602  trx_count = 0;
603  }
604 
605  _chain_db->precompute_parallel( transaction_message.trx ).wait();
606  _chain_db->push_transaction( transaction_message.trx );
607 } FC_CAPTURE_AND_RETHROW( (transaction_message) ) }
608 
609 void application_impl::handle_message(const message& message_to_process)
610 {
611  // not a transaction, not a block
612  FC_THROW( "Invalid Message Type" );
613 }
614 
616 {
617  uint32_t block_num = block_header::num_from_id(block_id);
618  block_id_type block_id_in_preferred_chain = _chain_db->get_block_id_for_num(block_num);
619  return block_id == block_id_in_preferred_chain;
620 }
621 
631 std::vector<item_hash_t> application_impl::get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
632  uint32_t& remaining_item_count,
633  uint32_t limit)
634 { try {
635  vector<block_id_type> result;
636  remaining_item_count = 0;
637  if( _chain_db->head_block_num() == 0 )
638  return result;
639 
640  result.reserve(limit);
641  block_id_type last_known_block_id;
642 
643  if (blockchain_synopsis.empty() ||
644  (blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
645  {
646  // peer has sent us an empty synopsis meaning they have no blocks.
647  // A bug in old versions would cause them to send a synopsis containing block 000000000
648  // when they had an empty blockchain, so pretend they sent the right thing here.
649 
650  // do nothing, leave last_known_block_id set to zero
651  }
652  else
653  {
654  bool found_a_block_in_synopsis = false;
655  for (const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis))
656  if (block_id_in_synopsis == block_id_type() ||
657  (_chain_db->is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis)))
658  {
659  last_known_block_id = block_id_in_synopsis;
660  found_a_block_in_synopsis = true;
661  break;
662  }
663  if (!found_a_block_in_synopsis)
664  FC_THROW_EXCEPTION( graphene::net::peer_is_on_an_unreachable_fork,
665  "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis" );
666  }
667  for( uint32_t num = block_header::num_from_id(last_known_block_id);
668  num <= _chain_db->head_block_num() && result.size() < limit;
669  ++num )
670  if( num > 0 )
671  result.push_back(_chain_db->get_block_id_for_num(num));
672 
673  if( !result.empty() && block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
674  remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
675 
676  return result;
677 } FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }
678 
682 message application_impl::get_item(const item_id& id)
683 { try {
684  // ilog("Request for item ${id}", ("id", id));
685  if( id.item_type == graphene::net::block_message_type )
686  {
687  auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
688  if( !opt_block )
689  elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}",
690  ("id", id.item_hash)("id2", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));
691  FC_ASSERT( opt_block.valid() );
692  // ilog("Serving up block #${num}", ("num", opt_block->block_num()));
693  return block_message(std::move(*opt_block));
694  }
695  return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
696 } FC_CAPTURE_AND_RETHROW( (id) ) }
697 
699 {
700  return _chain_db->get_chain_id();
701 }
702 
761 std::vector<item_hash_t> application_impl::get_blockchain_synopsis(const item_hash_t& reference_point,
762  uint32_t number_of_blocks_after_reference_point)
763 { try {
764  std::vector<item_hash_t> synopsis;
765  synopsis.reserve(30);
766  uint32_t high_block_num;
767  uint32_t non_fork_high_block_num;
768  uint32_t low_block_num = _chain_db->last_non_undoable_block_num();
769  std::vector<block_id_type> fork_history;
770 
771  if (reference_point != item_hash_t())
772  {
773  // the node is asking for a summary of the block chain up to a specified
774  // block, which may or may not be on a fork
775  // for now, assume it's not on a fork
776  if (is_included_block(reference_point))
777  {
778  // reference_point is a block we know about and is on the main chain
779  uint32_t reference_point_block_num = block_header::num_from_id(reference_point);
780  assert(reference_point_block_num > 0);
781  high_block_num = reference_point_block_num;
782  non_fork_high_block_num = high_block_num;
783 
784  if (reference_point_block_num < low_block_num)
785  {
786  // we're on the same fork (at least as far as reference_point) but we've passed
787  // reference point and could no longer undo that far if we diverged after that
788  // block. This should probably only happen due to a race condition where
789  // the network thread calls this function, and then immediately pushes a bunch of blocks,
790  // then the main thread finally processes this function.
791  // with the current framework, there's not much we can do to tell the network
792  // thread what our current head block is, so we'll just pretend that
793  // our head is actually the reference point.
794  // this *may* enable us to fetch blocks that we're unable to push, but that should
795  // be a rare case (and correctly handled)
796  low_block_num = reference_point_block_num;
797  }
798  }
799  else
800  {
801  // block is a block we know about, but it is on a fork
802  try
803  {
804  fork_history = _chain_db->get_block_ids_on_fork(reference_point);
805  // returns a vector where the last element is the common ancestor with the preferred chain,
806  // and the first element is the reference point you passed in
807  assert(fork_history.size() >= 2);
808 
809  if( fork_history.front() != reference_point )
810  {
811  edump( (fork_history)(reference_point) );
812  assert(fork_history.front() == reference_point);
813  }
814  block_id_type last_non_fork_block = fork_history.back();
815  fork_history.pop_back(); // remove the common ancestor
816  boost::reverse(fork_history);
817 
818  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?)
819  non_fork_high_block_num = 0;
820  else
821  non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);
822 
823  high_block_num = non_fork_high_block_num + fork_history.size();
824  assert(high_block_num == block_header::num_from_id(fork_history.back()));
825  }
826  catch (const fc::exception& e)
827  {
828  // unable to get fork history for some reason. maybe not linked?
829  // we can't return a synopsis of its chain
830  elog( "Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}",
831  ("hash", reference_point)("exception", e) );
832  throw;
833  }
834  if (non_fork_high_block_num < low_block_num)
835  {
836  wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
837  "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
838  ("low_block_num", low_block_num)
839  ("non_fork_high_block_num", non_fork_high_block_num));
840  FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
841  }
842  }
843  }
844  else
845  {
846  // no reference point specified, summarize the whole block chain
847  high_block_num = _chain_db->head_block_num();
848  non_fork_high_block_num = high_block_num;
849  if (high_block_num == 0)
850  return synopsis; // we have no blocks
851  }
852 
853  if( low_block_num == 0)
854  low_block_num = 1;
855 
856  // at this point:
857  // low_block_num is the block before the first block we can undo,
858  // 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)
859  // high_block_num is the block number of the reference block, or the end of the chain if no reference provided
860 
861  // true_high_block_num is the ending block number after the network code appends any item ids it
862  // knows about that we don't
863  uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
864  do
865  {
866  // for each block in the synopsis, figure out where to pull the block id from.
867  // if it's <= non_fork_high_block_num, we grab it from the main blockchain;
868  // if it's not, we pull it from the fork history
869  if (low_block_num <= non_fork_high_block_num)
870  synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));
871  else
872  synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);
873  low_block_num += (true_high_block_num - low_block_num + 2) / 2;
874  }
875  while (low_block_num <= high_block_num);
876 
877  //idump((synopsis));
878  return synopsis;
880 
888 void application_impl::sync_status(uint32_t item_type, uint32_t item_count)
889 {
890  // any status reports to GUI go here
891 }
892 
897 {
898  // any status reports to GUI go here
899 }
900 
902 { try {
903  return block_header::num_from_id(block_id);
904 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
905 
911 { try {
912  auto opt_block = _chain_db->fetch_block_by_id( block_id );
913  if( opt_block.valid() ) return opt_block->timestamp;
914  return fc::time_point_sec::min();
915 } FC_CAPTURE_AND_RETHROW( (block_id) ) }
916 
918 {
919  return _chain_db->head_block_id();
920 }
921 
923 {
924  return 0; // there are no forks in graphene
925 }
926 
927 void application_impl::error_encountered(const std::string& message, const fc::oexception& error)
928 {
929  // notify GUI or something cool
930 }
931 
933 {
934  return _chain_db->get_global_properties().parameters.block_interval;
935 }
936 
937 
938 
939 } } } // namespace graphene namespace app namespace detail
940 
941 namespace graphene { namespace app {
942 
944  : my(new detail::application_impl(this))
945 {}
946 
948 {
949  if( my->_p2p_network )
950  {
951  my->_p2p_network->close();
952  my->_p2p_network.reset();
953  }
954  if( my->_chain_db )
955  {
956  my->_chain_db->close();
957  }
958 }
959 
960 void application::set_program_options(boost::program_options::options_description& command_line_options,
961  boost::program_options::options_description& configuration_file_options) const
962 {
963  configuration_file_options.add_options()
964  ("p2p-endpoint", bpo::value<string>(), "Endpoint for P2P node to listen on")
965  ("seed-node,s", bpo::value<vector<string>>()->composing(),
966  "P2P nodes to connect to on startup (may specify multiple times)")
967  ("seed-nodes", bpo::value<string>()->composing(),
968  "JSON array of P2P nodes to connect to on startup")
969  ("checkpoint,c", bpo::value<vector<string>>()->composing(),
970  "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
971  ("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"),
972  "Endpoint for websocket RPC to listen on")
973  ("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"),
974  "Endpoint for TLS websocket RPC to listen on")
975  ("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
976  ("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
977  ("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
978  ("dbg-init-key", bpo::value<string>(), "Block signing key to use for init witnesses, overrides genesis file")
979  ("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
980  ("io-threads", bpo::value<uint16_t>()->implicit_value(0), "Number of IO threads, default to 0 for auto-configuration")
981  ("enable-subscribe-to-all", bpo::value<bool>()->implicit_value(true),
982  "Whether allow API clients to subscribe to universal object creation and removal events")
983  ("enable-standby-votes-tracking", bpo::value<bool>()->implicit_value(true),
984  "Whether to enable tracking of votes of standby witnesses and committee members. "
985  "Set it to true to provide accurate data to API clients, set to false for slightly better performance.")
986  ("api-limit-get-account-history-operations",boost::program_options::value<uint64_t>()->default_value(100),
987  "For history_api::get_account_history_operations to set max limit value")
988  ("api-limit-get-account-history",boost::program_options::value<uint64_t>()->default_value(100),
989  "For history_api::get_account_history to set max limit value")
990  ("api-limit-get-grouped-limit-orders",boost::program_options::value<uint64_t>()->default_value(101),
991  "For orders_api::get_grouped_limit_orders to set max limit value")
992  ("api-limit-get-relative-account-history",boost::program_options::value<uint64_t>()->default_value(100),
993  "For history_api::get_relative_account_history to set max limit value")
994  ("api-limit-get-account-history-by-operations",boost::program_options::value<uint64_t>()->default_value(100),
995  "For history_api::get_account_history_by_operations to set max limit value")
996  ("api-limit-get-asset-holders",boost::program_options::value<uint64_t>()->default_value(100),
997  "For asset_api::get_asset_holders to set max limit value")
998  ("api-limit-get-key-references",boost::program_options::value<uint64_t>()->default_value(100),
999  "For database_api_impl::get_key_references to set max limit value")
1000  ("api-limit-get-htlc-by",boost::program_options::value<uint64_t>()->default_value(100),
1001  "For database_api_impl::get_htlc_by_from and get_htlc_by_to to set max limit value")
1002  ("api-limit-get-full-accounts",boost::program_options::value<uint64_t>()->default_value(50),
1003  "For database_api_impl::get_full_accounts to set max accounts to query at once")
1004  ("api-limit-get-full-accounts-lists",boost::program_options::value<uint64_t>()->default_value(500),
1005  "For database_api_impl::get_full_accounts to set max items to return in the lists")
1006  ("api-limit-get-call-orders",boost::program_options::value<uint64_t>()->default_value(300),
1007  "For database_api_impl::get_call_orders and get_call_orders_by_account to set max limit value")
1008  ("api-limit-get-settle-orders",boost::program_options::value<uint64_t>()->default_value(300),
1009  "For database_api_impl::get_settle_orders and get_settle_orders_by_account to set max limit value")
1010  ("api-limit-get-assets",boost::program_options::value<uint64_t>()->default_value(101),
1011  "For database_api_impl::list_assets and get_assets_by_issuer to set max limit value")
1012  ("api-limit-get-limit-orders",boost::program_options::value<uint64_t>()->default_value(300),
1013  "For database_api_impl::get_limit_orders to set max limit value")
1014  ("api-limit-get-limit-orders-by-account",boost::program_options::value<uint64_t>()->default_value(101),
1015  "For database_api_impl::get_limit_orders_by_account to set max limit value")
1016  ("api-limit-get-order-book",boost::program_options::value<uint64_t>()->default_value(50),
1017  "For database_api_impl::get_order_book to set max limit value")
1018  ("api-limit-lookup-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1019  "For database_api_impl::lookup_accounts to set max limit value")
1020  ("api-limit-lookup-witness-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1021  "For database_api_impl::lookup_witness_accounts to set max limit value")
1022  ("api-limit-lookup-committee-member-accounts",boost::program_options::value<uint64_t>()->default_value(1000),
1023  "For database_api_impl::lookup_committee_member_accounts to set max limit value")
1024  ("api-limit-lookup-vote-ids",boost::program_options::value<uint64_t>()->default_value(1000),
1025  "For database_api_impl::lookup_vote_ids to set max limit value")
1026  ("api-limit-get-account-limit-orders",boost::program_options::value<uint64_t>()->default_value(101),
1027  "For database_api_impl::get_account_limit_orders to set max limit value")
1028  ("api-limit-get-collateral-bids",boost::program_options::value<uint64_t>()->default_value(100),
1029  "For database_api_impl::get_collateral_bids to set max limit value")
1030  ("api-limit-get-top-markets",boost::program_options::value<uint64_t>()->default_value(100),
1031  "For database_api_impl::get_top_markets to set max limit value")
1032  ("api-limit-get-trade-history",boost::program_options::value<uint64_t>()->default_value(100),
1033  "For database_api_impl::get_trade_history to set max limit value")
1034  ("api-limit-get-trade-history-by-sequence",boost::program_options::value<uint64_t>()->default_value(100),
1035  "For database_api_impl::get_trade_history_by_sequence to set max limit value")
1036  ("api-limit-get-withdraw-permissions-by-giver",boost::program_options::value<uint64_t>()->default_value(101),
1037  "For database_api_impl::get_withdraw_permissions_by_giver to set max limit value")
1038  ("api-limit-get-withdraw-permissions-by-recipient",boost::program_options::value<uint64_t>()->default_value(101),
1039  "For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value")
1040  ;
1041  command_line_options.add(configuration_file_options);
1042  command_line_options.add_options()
1043  ("replay-blockchain", "Rebuild object graph by replaying all blocks without validation")
1044  ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation")
1045  ("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
1046  ("force-validate", "Force validation of all transactions during normal operation")
1047  ("genesis-timestamp", bpo::value<uint32_t>(),
1048  "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)")
1049  ;
1050  command_line_options.add(_cli_options);
1051  configuration_file_options.add(_cfg_options);
1052 }
1053 
1054 void application::initialize(const fc::path& data_dir, const boost::program_options::variables_map& options)
1055 {
1056  my->_data_dir = data_dir;
1057  my->_options = &options;
1058 
1059  if ( options.count("io-threads") )
1060  {
1061  const uint16_t num_threads = options["io-threads"].as<uint16_t>();
1063  }
1064 }
1065 
1067 {
1068  try {
1069  my->startup();
1070  } catch ( const fc::exception& e ) {
1071  elog( "${e}", ("e",e.to_detail_string()) );
1072  throw;
1073  } catch ( ... ) {
1074  elog( "unexpected exception" );
1075  throw;
1076  }
1077 }
1078 
1080 {
1081  try {
1082  my->set_api_limit();
1083  } catch ( const fc::exception& e ) {
1084  elog( "${e}", ("e",e.to_detail_string()) );
1085  throw;
1086  } catch ( ... ) {
1087  elog( "unexpected exception" );
1088  throw;
1089  }
1090 }
1091 std::shared_ptr<abstract_plugin> application::get_plugin(const string& name) const
1092 {
1093  return my->_active_plugins[name];
1094 }
1095 
1096 bool application::is_plugin_enabled(const string& name) const
1097 {
1098  return !(my->_active_plugins.find(name) == my->_active_plugins.end());
1099 }
1100 
1102 {
1103  return my->_p2p_network;
1104 }
1105 
1106 std::shared_ptr<chain::database> application::chain_database() const
1107 {
1108  return my->_chain_db;
1109 }
1110 
1111 void application::set_block_production(bool producing_blocks)
1112 {
1113  my->_is_block_producer = producing_blocks;
1114 }
1115 
1117 {
1118  return my->get_api_access_info( username );
1119 }
1120 
1121 void application::set_api_access_info(const string& username, api_access_info&& permissions)
1122 {
1123  my->set_api_access_info(username, std::move(permissions));
1124 }
1125 
1127 {
1128  return my->_is_finished_syncing;
1129 }
1130 
1132 {
1133  FC_ASSERT(my->_available_plugins[name], "Unknown plugin '" + name + "'");
1134  my->_active_plugins[name] = my->_available_plugins[name];
1135  my->_active_plugins[name]->plugin_set_app(this);
1136 }
1137 
1138 void graphene::app::application::add_available_plugin(std::shared_ptr<graphene::app::abstract_plugin> p)
1139 {
1140  my->_available_plugins[p->plugin_name()] = p;
1141 }
1142 
1144 {
1145  for( auto& entry : my->_active_plugins )
1146  entry.second->plugin_shutdown();
1147  return;
1148 }
1150 {
1151  if( my->_p2p_network )
1152  my->_p2p_network->close();
1153  if( my->_chain_db )
1154  {
1155  my->_chain_db->close();
1156  my->_chain_db = nullptr;
1157  }
1158 }
1159 
1160 void application::initialize_plugins( const boost::program_options::variables_map& options )
1161 {
1162  for( auto& entry : my->_active_plugins )
1163  entry.second->plugin_initialize( options );
1164  return;
1165 }
1166 
1168 {
1169  for( auto& entry : my->_active_plugins )
1170  {
1171  entry.second->plugin_startup();
1172  ilog( "Plugin ${name} started", ( "name", entry.second->plugin_name() ) );
1173  }
1174  return;
1175 }
1176 
1178 {
1179  return my->_app_options;
1180 }
1181 
1182 // namespace detail
1183 } }
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:41
#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