BitShares-Core  7.0.2
BitShares blockchain node software and command-line wallet software
wallet_api_impl.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include <boost/algorithm/string/replace.hpp>
25 #include <boost/range/adaptors.hpp>
26 
28 #include <fc/popcount.hpp>
29 #include <fc/git_revision.hpp>
31 #include <fc/io/fstream.hpp>
32 
34 #include "wallet_api_impl.hpp"
36 
37 #ifndef WIN32
38 # include <sys/types.h>
39 # include <sys/stat.h>
40 #endif
41 
42 // explicit instantiation for later use
43 namespace fc {
44  template class api<graphene::wallet::wallet_api, identity_member_with_optionals>;
45 }
46 
47 /****
48  * General methods for wallet impl object (ctor, info, about, wallet file, etc.)
49  */
50 
51 namespace graphene { namespace wallet { namespace detail {
52 
53  wallet_api_impl::wallet_api_impl( wallet_api& s, const wallet_data& initial_data, fc::api<login_api> rapi )
54  : self(s),
55  _chain_id(initial_data.chain_id),
56  _remote_api(rapi),
57  _remote_db(rapi->database()),
58  _remote_net_broadcast(rapi->network_broadcast()),
59  _remote_hist(rapi->history())
60  {
61  try {
62  _custom_operations = rapi->custom_operations();
63  }
64  catch(const fc::exception& e)
65  {
66  wlog("Custom operations API is not active on server.");
67  }
68  chain_id_type remote_chain_id = _remote_db->get_chain_id();
69  if( remote_chain_id != _chain_id )
70  {
71  FC_THROW( "Remote server gave us an unexpected chain_id",
72  ("remote_chain_id", remote_chain_id)
73  ("chain_id", _chain_id) );
74  }
75  init_prototype_ops();
76 
77  _remote_db->set_block_applied_callback( [this](const variant& block_id )
78  {
79  on_block_applied( block_id );
80  } );
81 
82  _wallet.chain_id = _chain_id;
83  _wallet.ws_server = initial_data.ws_server;
84  _wallet.ws_user = initial_data.ws_user;
85  _wallet.ws_password = initial_data.ws_password;
86  }
87 
88  wallet_api_impl::~wallet_api_impl()
89  {
90  try
91  {
92  _remote_db->cancel_all_subscriptions();
93  }
94  catch (const fc::exception& e)
95  {
96  // Right now the wallet_api has no way of knowing if the connection to the
97  // witness has already disconnected (via the witness node exiting first).
98  // If it has exited, cancel_all_subscriptsions() will throw and there's
99  // nothing we can do about it.
100  // dlog("Caught exception ${e} while canceling database subscriptions", ("e", e));
101  }
102  }
103 
104  fc::variant wallet_api_impl::info() const
105  {
106  auto chain_props = get_chain_properties();
107  auto global_props = get_global_properties();
108  auto dynamic_props = get_dynamic_global_properties();
110  result["head_block_num"] = dynamic_props.head_block_number;
111  result["head_block_id"] = fc::variant(dynamic_props.head_block_id, 1);
112  result["head_block_age"] = fc::get_approximate_relative_time_string(dynamic_props.time,
113  time_point_sec(time_point::now()),
114  " old");
115  result["next_maintenance_time"] =
116  fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time);
117  result["chain_id"] = chain_props.chain_id;
118  stringstream participation;
119  participation << fixed << std::setprecision(2) << (100.0*fc::popcount(dynamic_props.recent_slots_filled)) / 128.0;
120  result["participation"] = participation.str();
121  result["active_witnesses"] = fc::variant(global_props.active_witnesses, GRAPHENE_MAX_NESTED_OBJECTS);
122  result["active_committee_members"] =
123  fc::variant(global_props.active_committee_members, GRAPHENE_MAX_NESTED_OBJECTS);
124  return result;
125  }
126 
127  /***
128  * @brief return basic information about this program
129  */
130  fc::variant_object wallet_api_impl::about() const
131  {
132  string client_version( graphene::utilities::git_revision_description );
133  const size_t pos = client_version.find( '/' );
134  if( pos != string::npos && client_version.size() > pos )
135  client_version = client_version.substr( pos + 1 );
136 
138  //result["blockchain_name"] = BLOCKCHAIN_NAME;
139  //result["blockchain_description"] = BTS_BLOCKCHAIN_DESCRIPTION;
140  result["client_version"] = client_version;
141  result["graphene_revision"] = graphene::utilities::git_revision_sha;
142  result["graphene_revision_age"] = fc::get_approximate_relative_time_string( fc::time_point_sec(
144  result["fc_revision"] = fc::git_revision_sha;
147  result["compile_date"] = "compiled on " __DATE__ " at " __TIME__;
148  result["boost_version"] = boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", ".");
149  result["openssl_version"] = OPENSSL_VERSION_TEXT;
150 
151  std::string bitness = boost::lexical_cast<std::string>(8 * sizeof(int*)) + "-bit";
152 #if defined(__APPLE__)
153  std::string os = "osx";
154 #elif defined(__linux__)
155  std::string os = "linux";
156 #elif defined(_MSC_VER)
157  std::string os = "win32";
158 #else
159  std::string os = "other";
160 #endif
161  result["build"] = os + " " + bitness;
162 
163  return result;
164  }
165 
166  void wallet_api_impl::quit()
167  {
168  ilog( "Quitting Cli Wallet ..." );
169 
170  throw fc::canceled_exception();
171  }
172 
173  chain_property_object wallet_api_impl::get_chain_properties() const
174  {
175  return _remote_db->get_chain_properties();
176  }
177  global_property_object wallet_api_impl::get_global_properties() const
178  {
179  return _remote_db->get_global_properties();
180  }
181  dynamic_global_property_object wallet_api_impl::get_dynamic_global_properties() const
182  {
183  return _remote_db->get_dynamic_global_properties();
184  }
185 
186  void wallet_api_impl::on_block_applied( const variant& block_id )
187  {
188  fc::async([this]{resync();}, "Resync after block");
189  }
190 
191  void wallet_api_impl::set_operation_fees( signed_transaction& tx, const fee_schedule& s ) const
192  {
193  for( auto& op : tx.operations )
194  s.set_fee(op);
195  }
196 
197  operation wallet_api_impl::get_prototype_operation( string operation_name )
198  {
199  auto it = _prototype_ops.find( operation_name );
200  if( it == _prototype_ops.end() )
201  FC_THROW("Unsupported operation: \"${operation_name}\"", ("operation_name", operation_name));
202  return it->second;
203  }
204 
205  void wallet_api_impl::init_prototype_ops()
206  {
207  operation op;
208  int64_t op_count = op.count();
209  for( int64_t t=0; t<op_count; t++ )
210  {
211  op.set_which( t );
212  op.visit( op_prototype_visitor(t, _prototype_ops) );
213  }
214  return;
215  }
216 
217  int wallet_api_impl::find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key)
218  {
219  int first_unused_index = 0;
220  int number_of_consecutive_unused_keys = 0;
221  for (int key_index = 0; ; ++key_index)
222  {
223  fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index);
224  graphene::chain::public_key_type derived_public_key = derived_private_key.get_public_key();
225  if( _keys.find(derived_public_key) == _keys.end() )
226  {
227  if (number_of_consecutive_unused_keys)
228  {
229  ++number_of_consecutive_unused_keys;
230  if (number_of_consecutive_unused_keys > 5)
231  return first_unused_index;
232  }
233  else
234  {
235  first_unused_index = key_index;
236  number_of_consecutive_unused_keys = 1;
237  }
238  }
239  else
240  {
241  // key_index is used
242  first_unused_index = 0;
243  number_of_consecutive_unused_keys = 0;
244  }
245  }
246  }
247 
248  void wallet_api_impl::enable_umask_protection()
249  {
250 #ifdef __unix__
251  _old_umask = umask( S_IRWXG | S_IRWXO );
252 #endif
253  }
254 
255  void wallet_api_impl::disable_umask_protection()
256  {
257 #ifdef __unix__
258  umask( _old_umask );
259 #endif
260  }
261 
262  bool wallet_api_impl::copy_wallet_file( string destination_filename )
263  {
264  fc::path src_path = get_wallet_filename();
265  if( !fc::exists( src_path ) )
266  return false;
267  fc::path dest_path = destination_filename + _wallet_filename_extension;
268  int suffix = 0;
269  while( fc::exists(dest_path) )
270  {
271  ++suffix;
272  dest_path = destination_filename + "-" + to_string( suffix ) + _wallet_filename_extension;
273  }
274  wlog( "backing up wallet ${src} to ${dest}",
275  ("src", src_path)
276  ("dest", dest_path) );
277 
278  fc::path dest_parent = fc::absolute(dest_path).parent_path();
279  try
280  {
281  enable_umask_protection();
282  if( !fc::exists( dest_parent ) )
283  fc::create_directories( dest_parent );
284  fc::copy( src_path, dest_path );
285  disable_umask_protection();
286  }
287  catch(...)
288  {
289  disable_umask_protection();
290  throw;
291  }
292  return true;
293  }
294 
295  /***
296  * @brief returns true if the wallet is unlocked
297  */
298  bool wallet_api_impl::is_locked()const
299  {
300  return _checksum == fc::sha512();
301  }
302 
303  void wallet_api_impl::resync()
304  {
305  fc::scoped_lock<fc::mutex> lock(_resync_mutex);
306  // this method is used to update wallet_data annotations
307  // e.g. wallet has been restarted and was not notified
308  // of events while it was down
309  //
310  // everything that is done "incremental style" when a push
311  // notification is received, should also be done here
312  // "batch style" by querying the blockchain
313 
314  if( !_wallet.pending_account_registrations.empty() )
315  {
316  // make a vector of the account names pending registration
317  std::vector<string> pending_account_names =
318  boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_account_registrations));
319 
320  // look those up on the blockchain
321  std::vector<fc::optional<graphene::chain::account_object >>
322  pending_account_objects = _remote_db->lookup_account_names( pending_account_names );
323 
324  // if any of them exist, claim them
325  for( const fc::optional<graphene::chain::account_object>& optional_account : pending_account_objects )
326  if( optional_account )
327  claim_registered_account(*optional_account);
328  }
329 
330  if (!_wallet.pending_witness_registrations.empty())
331  {
332  // make a vector of the owner accounts for witnesses pending registration
333  std::vector<string> pending_witness_names =
334  boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_witness_registrations));
335 
336  // look up the owners on the blockchain
337  std::vector<fc::optional<graphene::chain::account_object>> owner_account_objects =
338  _remote_db->lookup_account_names(pending_witness_names);
339 
340  // if any of them have registered witnesses, claim them
341  for( const fc::optional<graphene::chain::account_object>& optional_account : owner_account_objects )
342  if (optional_account)
343  {
344  auto account_id = std::string(optional_account->id);
345  fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(account_id);
346  if (witness_obj)
347  claim_registered_witness(optional_account->name);
348  }
349  }
350  }
351 
352  string wallet_api_impl::get_wallet_filename() const
353  {
354  return _wallet_filename;
355  }
356 
357  bool wallet_api_impl::load_wallet_file(string wallet_filename)
358  {
359  // TODO: Merge imported wallet with existing wallet,
360  // instead of replacing it
361  if( wallet_filename == "" )
362  wallet_filename = _wallet_filename;
363 
364  if( ! fc::exists( wallet_filename ) )
365  return false;
366 
367  _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >( 2 * GRAPHENE_MAX_NESTED_OBJECTS );
368  if( _wallet.chain_id != _chain_id )
369  FC_THROW( "Wallet chain ID does not match",
370  ("wallet.chain_id", _wallet.chain_id)
371  ("chain_id", _chain_id) );
372 
373  size_t account_pagination = 100;
374  vector< std::string > account_ids_to_send;
375  size_t n = _wallet.my_accounts.size();
376  account_ids_to_send.reserve( std::min( account_pagination, n ) );
377  auto it = _wallet.my_accounts.begin();
378 
379  for( size_t start=0; start<n; start+=account_pagination )
380  {
381  size_t end = std::min( start+account_pagination, n );
382  assert( end > start );
383  account_ids_to_send.clear();
384  std::vector< account_object > old_accounts;
385  for( size_t i=start; i<end; i++ )
386  {
387  assert( it != _wallet.my_accounts.end() );
388  old_accounts.push_back( *it );
389  auto account_id = std::string(old_accounts.back().id);
390  account_ids_to_send.push_back( account_id );
391  ++it;
392  }
393  std::vector< optional< account_object > > accounts = _remote_db->get_accounts(account_ids_to_send, {});
394  // server response should be same length as request
395  FC_ASSERT( accounts.size() == account_ids_to_send.size() );
396  size_t i = 0;
397  for( const optional< account_object >& acct : accounts )
398  {
399  account_object& old_acct = old_accounts[i];
400  if( !acct.valid() )
401  {
402  elog( "Could not find account ${id} : \"${name}\" does not exist on the chain!",
403  ("id", old_acct.id)("name", old_acct.name) );
404  i++;
405  continue;
406  }
407  // this check makes sure the server didn't send results
408  // in a different order, or accounts we didn't request
409  FC_ASSERT( acct->id == old_acct.id );
410  if( fc::json::to_string(*acct) != fc::json::to_string(old_acct) )
411  {
412  wlog( "Account ${id} : \"${name}\" updated on chain", ("id", acct->id)("name", acct->name) );
413  }
414  _wallet.update_account( *acct );
415  i++;
416  }
417  }
418 
419  return true;
420  }
421 
422  void wallet_api_impl::save_wallet_file(string wallet_filename)
423  {
424  //
425  // Serialize in memory, then save to disk
426  //
427  // This approach lessens the risk of a partially written wallet
428  // if exceptions are thrown in serialization
429  //
430 
431  encrypt_keys();
432 
433  if( wallet_filename == "" )
434  wallet_filename = _wallet_filename;
435 
436  wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) );
437 
438  string data = fc::json::to_pretty_string( _wallet );
439 
440  try
441  {
442  enable_umask_protection();
443  //
444  // Parentheses on the following declaration fails to compile,
445  // due to the Most Vexing Parse. Thanks, C++
446  //
447  // http://en.wikipedia.org/wiki/Most_vexing_parse
448  //
449  std::string tmp_wallet_filename = wallet_filename + ".tmp";
450  fc::ofstream outfile{ fc::path( tmp_wallet_filename ) };
451  outfile.write( data.c_str(), data.length() );
452  outfile.flush();
453  outfile.close();
454 
455  wlog( "saved successfully wallet to tmp file ${fn}", ("fn", tmp_wallet_filename) );
456 
457  std::string wallet_file_content;
458  fc::read_file_contents(tmp_wallet_filename, wallet_file_content);
459 
460  if (wallet_file_content == data) {
461  wlog( "validated successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) );
462 
463  fc::rename( tmp_wallet_filename, wallet_filename );
464 
465  wlog( "renamed successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) );
466  }
467  else
468  {
469  FC_THROW("tmp wallet file cannot be validated ${fn}", ("fn", tmp_wallet_filename) );
470  }
471 
472  wlog( "successfully saved wallet to file ${fn}", ("fn", wallet_filename) );
473 
474  disable_umask_protection();
475  }
476  catch(...)
477  {
478  string ws_password = _wallet.ws_password;
479  _wallet.ws_password = "";
480  wlog("wallet file content is next: ${data}", ("data", fc::json::to_pretty_string( _wallet ) ) );
481  _wallet.ws_password = ws_password;
482 
483  disable_umask_protection();
484  throw;
485  }
486  }
487 
488 }}} // namespace graphene::wallet::detail
graphene::protocol::transaction::operations
vector< operation > operations
Definition: transaction.hpp:89
fc::copy
void copy(const path &from, const path &to)
Definition: filesystem.cpp:241
git_revision.hpp
fc::variant_object
An order-perserving dictionary of variant's.
Definition: variant_object.hpp:20
graphene::db::object::id
object_id_type id
Definition: object.hpp:69
graphene::chain::dynamic_global_property_object
Maintains global state information (committee_member list, current fees)
Definition: global_property_object.hpp:62
graphene::chain::database
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
wlog
#define wlog(FORMAT,...)
Definition: logger.hpp:123
fc::exception
Used to generate a useful error report when an exception is thrown.
Definition: exception.hpp:56
fc::mutable_variant_object
An order-perserving dictionary of variant's.
Definition: variant_object.hpp:108
fc::scoped_lock
Definition: scoped_lock.hpp:5
fc::to_string
std::string to_string(double)
Definition: string.cpp:73
graphene::protocol::fee_schedule
contains all of the parameters necessary to calculate the fee for any operation
Definition: fee_schedule.hpp:173
fc::static_variant< transfer_operation, limit_order_create_operation, limit_order_cancel_operation, call_order_update_operation, fill_order_operation, account_create_operation, account_update_operation, account_whitelist_operation, account_upgrade_operation, account_transfer_operation, asset_create_operation, asset_update_operation, asset_update_bitasset_operation, asset_update_feed_producers_operation, asset_issue_operation, asset_reserve_operation, asset_fund_fee_pool_operation, asset_settle_operation, asset_global_settle_operation, asset_publish_feed_operation, witness_create_operation, witness_update_operation, proposal_create_operation, proposal_update_operation, proposal_delete_operation, withdraw_permission_create_operation, withdraw_permission_update_operation, withdraw_permission_claim_operation, withdraw_permission_delete_operation, committee_member_create_operation, committee_member_update_operation, committee_member_update_global_parameters_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, worker_create_operation, custom_operation, assert_operation, balance_claim_operation, override_transfer_operation, transfer_to_blind_operation, blind_transfer_operation, transfer_from_blind_operation, asset_settle_cancel_operation, asset_claim_fees_operation, fba_distribute_operation, bid_collateral_operation, execute_bid_operation, asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, htlc_redeemed_operation, htlc_extend_operation, htlc_refund_operation, custom_authority_create_operation, custom_authority_update_operation, custom_authority_delete_operation, ticket_create_operation, ticket_update_operation, liquidity_pool_create_operation, liquidity_pool_delete_operation, liquidity_pool_deposit_operation, liquidity_pool_withdraw_operation, liquidity_pool_exchange_operation, samet_fund_create_operation, samet_fund_delete_operation, samet_fund_update_operation, samet_fund_borrow_operation, samet_fund_repay_operation, credit_offer_create_operation, credit_offer_delete_operation, credit_offer_update_operation, credit_offer_accept_operation, credit_deal_repay_operation, credit_deal_expired_operation, liquidity_pool_update_operation, credit_deal_update_operation, limit_order_update_operation >
graphene::utilities::key_to_wif
std::string key_to_wif(const fc::sha256 &private_secret)
Definition: key_conversion.cpp:30
graphene::wallet::detail::derive_private_key
fc::ecc::private_key derive_private_key(const std::string &prefix_string, int sequence_number)
Definition: wallet_sign.cpp:60
fc
Definition: api.hpp:15
FC_THROW
#define FC_THROW( ...)
Definition: exception.hpp:366
fc::static_variant::count
static constexpr size_t count()
Definition: static_variant.hpp:319
scoped_lock.hpp
fc::sha256
Definition: sha256.hpp:10
graphene::utilities::git_revision_description
const char *const git_revision_description
fc::json::to_pretty_string
static string to_pretty_string(const variant &v, output_formatting format=stringify_large_ints_and_doubles, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:748
api_connection.hpp
wallet_api_impl.hpp
graphene::protocol::public_key_type
Definition: types.hpp:312
fc::json::to_string
static string to_string(const variant &v, output_formatting format=stringify_large_ints_and_doubles, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:650
fc::git_revision_unix_timestamp
const uint32_t git_revision_unix_timestamp
fc::ecc::private_key
an elliptic curve private key.
Definition: elliptic.hpp:89
graphene::protocol::signed_transaction
adds a signature to a transaction
Definition: transaction.hpp:134
fc::sha512
Definition: sha512.hpp:9
graphene::protocol::fee_schedule::set_fee
asset set_fee(operation &op, const price &core_exchange_rate=price::unit_price()) const
Definition: fee_schedule_set_fee.cpp:43
fc::path
wraps boost::filesystem::path to provide platform independent path manipulation.
Definition: filesystem.hpp:28
ilog
#define ilog(FORMAT,...)
Definition: logger.hpp:117
git_revision.hpp
wallet.hpp
graphene::chain::account_object
This class represents an account on the object graph.
Definition: account_object.hpp:180
graphene::utilities::git_revision_unix_timestamp
const uint32_t git_revision_unix_timestamp
fc::popcount
uint8_t popcount(uint64_t v)
Definition: popcount.cpp:31
fc::time_point_sec
Definition: time.hpp:74
fc::async
auto async(Functor &&f, const char *desc FC_TASK_NAME_DEFAULT_ARG, priority prio=priority()) -> fc::future< decltype(f())>
Definition: thread.hpp:227
fc::variant::as
T as(uint32_t max_depth) const
Definition: variant.hpp:337
graphene::wallet::detail::op_prototype_visitor
Definition: wallet_api_impl.hpp:71
fc::ecc::private_key::get_public_key
public_key get_public_key() const
Definition: elliptic_impl_priv.cpp:70
fc::get_approximate_relative_time_string
std::string get_approximate_relative_time_string(const time_point_sec &event_time, const time_point_sec &relative_to_time=fc::time_point::now(), const std::string &ago=" ago")
Definition: time.cpp:70
fc::absolute
path absolute(const path &p)
Definition: filesystem.cpp:341
FC_ASSERT
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
fc::ofstream
Definition: fstream.hpp:9
graphene::chain::account_object::name
string name
The account's name. This name must be unique among all account names on the graph....
Definition: account_object.hpp:209
fc::variant
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
Definition: variant.hpp:198
fc::read_file_contents
void read_file_contents(const fc::path &filename, std::string &result)
Definition: fstream.cpp:107
fc::path::parent_path
fc::path parent_path() const
Definition: filesystem.cpp:163
graphene::chain::global_property_object
Maintains global state information (committee_member list, current fees)
Definition: global_property_object.hpp:40
fstream.hpp
popcount.hpp
GRAPHENE_MAX_NESTED_OBJECTS
#define GRAPHENE_MAX_NESTED_OBJECTS
Definition: config.hpp:33
graphene::wallet::wallet_data
Definition: wallet_structs.hpp:150
fc::create_directories
void create_directories(const path &p)
Definition: filesystem.cpp:210
graphene::utilities::git_revision_sha
const char *const git_revision_sha
fc::static_variant::set_which
void set_which(tag_type tag)
Definition: static_variant.hpp:320
fc::static_variant::visit
visitor::result_type visit(visitor &v)
Definition: static_variant.hpp:256
fc::optional
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
graphene::chain::chain_property_object
Definition: chain_property_object.hpp:33
fc::json::from_file
static variant from_file(const fc::path &p, parse_type ptype=legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:767
fc::api
Definition: api.hpp:120
fc::git_revision_sha
const char *const git_revision_sha
fc::rename
void rename(const path &from, const path &to)
Definition: filesystem.cpp:302
graphene
Definition: api.cpp:48
fc::exists
bool exists(const path &p)
Definition: filesystem.cpp:209
elog
#define elog(FORMAT,...)
Definition: logger.hpp:129