/* * Copyright (c) 2015, Cryptonomex, Incn * All rights reserved. * * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, * are permitted until September 8, 2015, provided that the following conditions are met: * * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WIN32 # include # include #endif #define BRAIN_KEY_WORD_COUNT 16 namespace graphene { namespace wallet { namespace detail { // BLOCK TRX OP VOP struct operation_printer { private: ostream& out; const wallet_api_impl& wallet; operation_result result; void fee(const asset& a) const; public: operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) : out(out), wallet(wallet), result(r) {} typedef void result_type; template void operator()(const T& op)const { //balance_accumulator acc; //op.get_balance_delta( acc, result ); string op_name = fc::get_typename::name(); if( op_name.find_last_of(':') != string::npos ) op_name.erase(0, op_name.find_last_of(':')+1); out << op_name <<" "; // out << "balance delta: " << fc::json::to_string(acc.balance) <<" "; out << fc::json::to_string(op.fee_payer()) << " fee: " << fc::json::to_string(op.fee); } void operator()(const transfer_operation& op)const; void operator()(const account_create_operation& op)const; void operator()(const account_update_operation& op)const; void operator()(const asset_create_operation& op)const; }; template optional maybe_id( const string& name_or_id ) { if( std::isdigit( name_or_id.front() ) ) { try { return fc::variant(name_or_id).as(); } catch (const fc::exception&) { } } return optional(); } string address_to_shorthash( const address& addr ) { uint32_t x = addr.addr._hash[0]; static const char hd[] = "0123456789abcdef"; string result; result += hd[(x >> 0x1c) & 0x0f]; result += hd[(x >> 0x18) & 0x0f]; result += hd[(x >> 0x14) & 0x0f]; result += hd[(x >> 0x10) & 0x0f]; result += hd[(x >> 0x0c) & 0x0f]; result += hd[(x >> 0x08) & 0x0f]; result += hd[(x >> 0x04) & 0x0f]; result += hd[(x ) & 0x0f]; return result; } fc::ecc::private_key derive_private_key( const std::string& prefix_string, int sequence_number ) { std::string sequence_string = std::to_string(sequence_number); fc::sha512 h = fc::sha512::hash(prefix_string + " " + sequence_string); fc::ecc::private_key derived_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h)); return derived_key; } string normalize_brain_key( string s ) { size_t i = 0, n = s.length(); std::string result; char c; result.reserve( n ); bool preceded_by_whitespace = false; bool non_empty = false; while( i < n ) { c = s[i++]; switch( c ) { case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': preceded_by_whitespace = true; continue; case 'a': c = 'A'; break; case 'b': c = 'B'; break; case 'c': c = 'C'; break; case 'd': c = 'D'; break; case 'e': c = 'E'; break; case 'f': c = 'F'; break; case 'g': c = 'G'; break; case 'h': c = 'H'; break; case 'i': c = 'I'; break; case 'j': c = 'J'; break; case 'k': c = 'K'; break; case 'l': c = 'L'; break; case 'm': c = 'M'; break; case 'n': c = 'N'; break; case 'o': c = 'O'; break; case 'p': c = 'P'; break; case 'q': c = 'Q'; break; case 'r': c = 'R'; break; case 's': c = 'S'; break; case 't': c = 'T'; break; case 'u': c = 'U'; break; case 'v': c = 'V'; break; case 'w': c = 'W'; break; case 'x': c = 'X'; break; case 'y': c = 'Y'; break; case 'z': c = 'Z'; break; default: break; } if( preceded_by_whitespace && non_empty ) result.push_back(' '); result.push_back(c); preceded_by_whitespace = false; non_empty = true; } return result; } class wallet_api_impl { public: api_documentation method_documentation; private: void claim_registered_account(const account_object& account) { auto it = _wallet.pending_account_registrations.find( account.name ); FC_ASSERT( it != _wallet.pending_account_registrations.end() ); for (const std::string& wif_key : it->second) if( !import_key( account.name, wif_key ) ) { // somebody else beat our pending registration, there is // nothing we can do except log it and move on elog( "account ${name} registered by someone else first!", ("name", account.name) ); // might as well remove it from pending regs, // because there is now no way this registration // can become valid (even in the extremely rare // possibility of migrating to a fork where the // name is available, the user can always // manually re-register) } _wallet.pending_account_registrations.erase( it ); } // after a witness registration succeeds, this saves the private key in the wallet permanently // void claim_registered_witness(const std::string& witness_name) { auto iter = _wallet.pending_witness_registrations.find(witness_name); FC_ASSERT(iter != _wallet.pending_witness_registrations.end()); std::string wif_key = iter->second; // get the list key id this key is registered with in the chain fc::optional witness_private_key = wif_to_key(wif_key); FC_ASSERT(witness_private_key); auto pub_key = witness_private_key->get_public_key(); _keys[pub_key] = wif_key; _wallet.pending_witness_registrations.erase(iter); } fc::mutex _resync_mutex; void resync() { fc::scoped_lock lock(_resync_mutex); // this method is used to update wallet_data annotations // e.g. wallet has been restarted and was not notified // of events while it was down // // everything that is done "incremental style" when a push // notification is received, should also be done here // "batch style" by querying the blockchain if( !_wallet.pending_account_registrations.empty() ) { // make a vector of the account names pending registration std::vector pending_account_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_account_registrations)); // look those up on the blockchain std::vector> pending_account_objects = _remote_db->lookup_account_names( pending_account_names ); // if any of them exist, claim them for( const fc::optional& optional_account : pending_account_objects ) if( optional_account ) claim_registered_account(*optional_account); } if (!_wallet.pending_witness_registrations.empty()) { // make a vector of the owner accounts for witnesses pending registration std::vector pending_witness_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_witness_registrations)); // look up the owners on the blockchain std::vector> owner_account_objects = _remote_db->lookup_account_names(pending_witness_names); // if any of them have registered witnesses, claim them for( const fc::optional& optional_account : owner_account_objects ) if (optional_account) { fc::optional witness_obj = _remote_db->get_witness_by_account(optional_account->id); if (witness_obj) claim_registered_witness(optional_account->name); } } } void enable_umask_protection() { #ifdef __unix__ _old_umask = umask( S_IRWXG | S_IRWXO ); #endif } void disable_umask_protection() { #ifdef __unix__ umask( _old_umask ); #endif } map _builder_transactions; public: wallet_api& self; wallet_api_impl( wallet_api& s, fc::api rapi ) : self(s), _remote_api(rapi), _remote_db(rapi->database()), _remote_net_broadcast(rapi->network_broadcast()), _remote_hist(rapi->history()) { _remote_db->subscribe_to_objects( [=]( const fc::variant& obj ) { fc::async([this]{resync();}, "Resync after block"); }, {dynamic_global_property_id_type()} ); } virtual ~wallet_api_impl() { try { _remote_db->cancel_all_subscriptions(); } catch (const fc::exception& e) { // Right now the wallet_api has no way of knowing if the connection to the // witness has already disconnected (via the witness node exiting first). // If it has exited, cancel_all_subscriptsions() will throw and there's // nothing we can do about it. // dlog("Caught exception ${e} while canceling database subscriptions", ("e", e)); } } void encrypt_keys() { plain_keys data; data.keys = _keys; data.checksum = _checksum; auto plain_txt = fc::raw::pack(data); _wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt ); } bool copy_wallet_file( string destination_filename ) { fc::path src_path = get_wallet_filename(); if( !fc::exists( src_path ) ) return false; fc::path dest_path = destination_filename + _wallet_filename_extension; int suffix = 0; while( fc::exists(dest_path) ) { ++suffix; dest_path = destination_filename + "-" + to_string( suffix ) + _wallet_filename_extension; } wlog( "backing up wallet ${src} to ${dest}", ("src", src_path) ("dest", dest_path) ); fc::path dest_parent = fc::absolute(dest_path).parent_path(); try { enable_umask_protection(); if( !fc::exists( dest_parent ) ) fc::create_directories( dest_parent ); fc::copy( src_path, dest_path ); } catch(...) { disable_umask_protection(); throw; } return true; } bool is_locked()const { return _checksum == fc::sha512(); } template T get_object(object_id id)const { auto ob = _remote_db->get_objects({id}).front(); return ob.template as(); } void set_operation_fees( signed_transaction& tx, const fee_schedule& s ) { for( auto& op : tx.operations ) s.set_fee(op); } variant info() const { auto global_props = get_global_properties(); auto dynamic_props = get_dynamic_global_properties(); fc::mutable_variant_object result; result["head_block_num"] = dynamic_props.head_block_number; result["head_block_id"] = dynamic_props.head_block_id; result["head_block_age"] = fc::get_approximate_relative_time_string(dynamic_props.time, time_point_sec(time_point::now()), " old"); result["next_maintenance_time"] = fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time); result["chain_id"] = global_props.chain_id; result["active_witnesses"] = global_props.active_witnesses; result["active_delegates"] = global_props.active_delegates; result["entropy"] = dynamic_props.random; return result; } global_property_object get_global_properties() const { return _remote_db->get_global_properties(); } dynamic_global_property_object get_dynamic_global_properties() const { return _remote_db->get_dynamic_global_properties(); } account_object get_account(account_id_type id) const { if( _wallet.my_accounts.get().count(id) ) return *_wallet.my_accounts.get().find(id); auto rec = _remote_db->get_accounts({id}).front(); FC_ASSERT(rec); return *rec; } account_object get_account(string account_name_or_id) const { FC_ASSERT( account_name_or_id.size() > 0 ); if( auto id = maybe_id(account_name_or_id) ) { // It's an ID return get_account(*id); } else { // It's a name if( _wallet.my_accounts.get().count(account_name_or_id) ) return *_wallet.my_accounts.get().find(account_name_or_id); auto rec = _remote_db->lookup_account_names({account_name_or_id}).front(); FC_ASSERT( rec && rec->name == account_name_or_id ); return *rec; } } account_id_type get_account_id(string account_name_or_id) const { return get_account(account_name_or_id).get_id(); } optional find_asset(asset_id_type id)const { auto rec = _remote_db->get_assets({id}).front(); if( rec ) _asset_cache[id] = *rec; return rec; } optional find_asset(string asset_symbol_or_id)const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); if( auto id = maybe_id(asset_symbol_or_id) ) { // It's an ID return find_asset(*id); } else { // It's a symbol auto rec = _remote_db->lookup_asset_symbols({asset_symbol_or_id}).front(); if( rec ) { if( rec->symbol != asset_symbol_or_id ) return optional(); _asset_cache[rec->get_id()] = *rec; } return rec; } } asset_object get_asset(asset_id_type id)const { auto opt = find_asset(id); FC_ASSERT(opt); return *opt; } asset_object get_asset(string asset_symbol_or_id)const { auto opt = find_asset(asset_symbol_or_id); FC_ASSERT(opt); return *opt; } asset_id_type get_asset_id(string asset_symbol_or_id) const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); vector> opt_asset; if( std::isdigit( asset_symbol_or_id.front() ) ) return fc::variant(asset_symbol_or_id).as(); opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} ); FC_ASSERT( (opt_asset.size() > 0) && (opt_asset[0].valid()) ); return opt_asset[0]->id; } string get_wallet_filename() const { return _wallet_filename; } fc::ecc::private_key get_private_key(const public_key_type& id)const { auto it = _keys.find(id); FC_ASSERT( it != _keys.end() ); fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); FC_ASSERT( privkey ); return *privkey; } fc::ecc::private_key get_private_key_for_account(const account_object& account)const { vector active_keys = account.active.get_keys(); if (active_keys.size() != 1) FC_THROW("Expecting a simple authority with one active key"); return get_private_key(active_keys.front()); } // imports the private key into the wallet, and associate it in some way (?) with the // given account name. // @returns true if the key matches a current active/owner/memo key for the named // account, false otherwise (but it is stored either way) bool import_key(string account_name_or_id, string wif_key) { fc::optional optional_private_key = wif_to_key(wif_key); if (!optional_private_key) FC_THROW("Invalid private key ${key}", ("key", wif_key)); graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key(); account_object account = get_account( account_name_or_id ); // make a list of all current public keys for the named account flat_set all_keys_for_account; std::vector active_keys = account.active.get_keys(); std::vector owner_keys = account.owner.get_keys(); std::copy(active_keys.begin(), active_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); std::copy(owner_keys.begin(), owner_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); all_keys_for_account.insert(account.options.memo_key); _keys[wif_pub_key] = wif_key; if( _wallet.update_account(account) ) _remote_db->subscribe_to_objects([this](const fc::variant& v) { _wallet.update_account(v.as()); }, {account.id}); _wallet.extra_keys[account.id].insert(wif_pub_key); return all_keys_for_account.find(wif_pub_key) != all_keys_for_account.end(); } bool load_wallet_file(string wallet_filename = "") { // // TODO: Merge imported wallet with existing wallet, // instead of replacing it // if( wallet_filename == "" ) wallet_filename = _wallet_filename; if( ! fc::exists( wallet_filename ) ) return false; if( !_wallet.my_accounts.empty() ) _remote_db->unsubscribe_from_objects(_wallet.my_account_ids()); _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >(); if( !_wallet.my_accounts.empty() ) _remote_db->subscribe_to_objects([this](const fc::variant& v) { _wallet.update_account(v.as()); }, _wallet.my_account_ids()); return true; } void save_wallet_file(string wallet_filename = "") { // // Serialize in memory, then save to disk // // This approach lessens the risk of a partially written wallet // if exceptions are thrown in serialization // encrypt_keys(); if( wallet_filename == "" ) wallet_filename = _wallet_filename; wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); string data = fc::json::to_pretty_string( _wallet ); try { enable_umask_protection(); // // Parentheses on the following declaration fails to compile, // due to the Most Vexing Parse. Thanks, C++ // // http://en.wikipedia.org/wiki/Most_vexing_parse // fc::ofstream outfile{ fc::path( wallet_filename ) }; outfile.write( data.c_str(), data.length() ); outfile.flush(); outfile.close(); } catch(...) { disable_umask_protection(); throw; } } transaction_handle_type begin_builder_transaction() { int trx_handle = _builder_transactions.empty()? 0 : (--_builder_transactions.end())->first + 1; _builder_transactions[trx_handle]; return trx_handle; } void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) { FC_ASSERT(_builder_transactions.count(transaction_handle)); _builder_transactions[transaction_handle].operations.emplace_back(op); } void replace_operation_in_builder_transaction(transaction_handle_type handle, uint32_t operation_index, const operation& new_op) { FC_ASSERT(_builder_transactions.count(handle)); signed_transaction& trx = _builder_transactions[handle]; FC_ASSERT( operation_index < trx.operations.size()); trx.operations[operation_index] = new_op; } asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL) { FC_ASSERT(_builder_transactions.count(handle)); auto fee_asset_obj = get_asset(fee_asset); asset total_fee = fee_asset_obj.amount(0); auto gprops = _remote_db->get_global_properties().parameters; if( fee_asset_obj.get_id() != asset_id_type() ) { for( auto& op : _builder_transactions[handle].operations ) total_fee += gprops.current_fees->set_fee( op, fee_asset_obj.options.core_exchange_rate ); FC_ASSERT((total_fee * fee_asset_obj.options.core_exchange_rate).amount <= get_object(fee_asset_obj.dynamic_asset_data_id).fee_pool, "Cannot pay fees in ${asset}, as this asset's fee pool is insufficiently funded.", ("asset", fee_asset_obj.symbol)); } else { for( auto& op : _builder_transactions[handle].operations ) total_fee += gprops.current_fees->set_fee( op ); } return total_fee; } transaction preview_builder_transaction(transaction_handle_type handle) { FC_ASSERT(_builder_transactions.count(handle)); return _builder_transactions[handle]; } signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true) { FC_ASSERT(_builder_transactions.count(transaction_handle)); return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast); } signed_transaction propose_builder_transaction(transaction_handle_type handle, time_point_sec expiration = time_point::now() + fc::minutes(1), uint32_t review_period_seconds = 0, bool broadcast = true) { FC_ASSERT(_builder_transactions.count(handle)); proposal_create_operation op; op.expiration_time = expiration; signed_transaction& trx = _builder_transactions[handle]; std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops), [](const operation& op) -> op_wrapper { return op; }); if( review_period_seconds ) op.review_period_seconds = review_period_seconds; trx.operations = {op}; _remote_db->get_global_properties().parameters.current_fees->set_fee( trx.operations.front() ); return trx = sign_transaction(trx, broadcast); } void remove_builder_transaction(transaction_handle_type handle) { _builder_transactions.erase(handle); } signed_transaction register_account(string name, public_key_type owner, public_key_type active, string registrar_account, string referrer_account, uint8_t referrer_percent, bool broadcast = false) { try { FC_ASSERT( !self.is_locked() ); FC_ASSERT( is_valid_name(name) ); account_create_operation account_create_op; // TODO: process when pay_from_account is ID account_object registrar_account_object = this->get_account( registrar_account ); FC_ASSERT( registrar_account_object.is_lifetime_member() ); account_id_type registrar_account_id = registrar_account_object.id; account_object referrer_account_object = this->get_account( referrer_account ); account_create_op.referrer = referrer_account_object.id; account_create_op.referrer_percent = referrer_percent; account_create_op.registrar = registrar_account_id; account_create_op.name = name; account_create_op.owner = authority(1, owner, 1); account_create_op.active = authority(1, active, 1); account_create_op.options.memo_key = active; signed_transaction tx; tx.operations.push_back( account_create_op ); auto current_fees = _remote_db->get_global_properties().parameters.current_fees; set_operation_fees( tx, current_fees ); vector paying_keys = registrar_account_object.active.get_keys(); tx.validate(); for( public_key_type& key : paying_keys ) { auto it = _keys.find(key); if( it != _keys.end() ) { fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); if( !privkey.valid() ) { FC_ASSERT( false, "Malformed private key in _keys" ); } tx.sign( *privkey ); } } if( broadcast ) _remote_net_broadcast->broadcast_transaction( tx ); return tx; } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)(referrer_account)(referrer_percent)(broadcast) ) } signed_transaction upgrade_account(string name, bool broadcast) { try { FC_ASSERT( !self.is_locked() ); account_object account_obj = get_account(name); FC_ASSERT( !account_obj.is_lifetime_member() ); signed_transaction tx; account_upgrade_operation op; op.account_to_upgrade = account_obj.get_id(); op.upgrade_to_lifetime_member = true; tx.operations = {op}; set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (name) ) } // This function generates derived keys starting with index 0 and keeps incrementing // the index until it finds a key that isn't registered in the block chain. To be // safer, it continues checking for a few more keys to make sure there wasn't a short gap // caused by a failed registration or the like. int find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key) { int first_unused_index = 0; int number_of_consecutive_unused_keys = 0; for (int key_index = 0; ; ++key_index) { fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index); graphene::chain::public_key_type derived_public_key = derived_private_key.get_public_key(); if( _keys.find(derived_public_key) == _keys.end() ) { if (number_of_consecutive_unused_keys) { ++number_of_consecutive_unused_keys; if (number_of_consecutive_unused_keys > 5) return first_unused_index; } else { first_unused_index = key_index; number_of_consecutive_unused_keys = 1; } } else { // key_index is used first_unused_index = 0; number_of_consecutive_unused_keys = 0; } } } signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey, string account_name, string registrar_account, string referrer_account, bool broadcast = false, bool save_wallet = true) { try { int active_key_index = find_first_unused_derived_key_index(owner_privkey); fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index); int memo_key_index = find_first_unused_derived_key_index(active_privkey); fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index); graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key(); graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key(); graphene::chain::public_key_type memo_pubkey = memo_privkey.get_public_key(); account_create_operation account_create_op; // TODO: process when pay_from_account is ID account_object registrar_account_object = get_account( registrar_account ); account_id_type registrar_account_id = registrar_account_object.id; account_object referrer_account_object = get_account( referrer_account ); account_create_op.referrer = referrer_account_object.id; account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage; account_create_op.registrar = registrar_account_id; account_create_op.name = account_name; account_create_op.owner = authority(1, owner_pubkey, 1); account_create_op.active = authority(1, active_pubkey, 1); account_create_op.options.memo_key = memo_pubkey; // current_fee_schedule() // find_account(pay_from_account) // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule()); signed_transaction tx; tx.operations.push_back( account_create_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); vector paying_keys = registrar_account_object.active.get_keys(); tx.set_expiration(get_dynamic_global_properties().head_block_id); tx.validate(); for( public_key_type& key : paying_keys ) { auto it = _keys.find(key); if( it != _keys.end() ) { fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); tx.sign( *privkey ); } } // we do not insert owner_privkey here because // it is intended to only be used for key recovery _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey )); _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey )); if( save_wallet ) save_wallet_file(); if( broadcast ) _remote_net_broadcast->broadcast_transaction( tx ); return tx; } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) } signed_transaction create_account_with_brain_key(string brain_key, string account_name, string registrar_account, string referrer_account, bool broadcast = false) { try { FC_ASSERT( !self.is_locked() ); string normalized_brain_key = normalize_brain_key( brain_key ); // TODO: scan blockchain for accounts that exist with same brain key fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 ); return create_account_with_private_key(owner_privkey, account_name, registrar_account, referrer_account, broadcast); } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) } signed_transaction create_asset(string issuer, string symbol, uint8_t precision, asset_options common, fc::optional bitasset_opts, bool broadcast = false) { try { account_object issuer_account = get_account( issuer ); FC_ASSERT(!find_asset(symbol).valid(), "Asset with that symbol already exists!"); asset_create_operation create_op; create_op.issuer = issuer_account.id; create_op.symbol = symbol; create_op.precision = precision; create_op.common_options = common; create_op.bitasset_opts = bitasset_opts; signed_transaction tx; tx.operations.push_back( create_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) } signed_transaction update_asset(string symbol, optional new_issuer, asset_options new_options, bool broadcast /* = false */) { try { optional asset_to_update = find_asset(symbol); if (!asset_to_update) FC_THROW("No asset with that symbol exists!"); optional new_issuer_account_id; if (new_issuer) { account_object new_issuer_account = get_account(*new_issuer); new_issuer_account_id = new_issuer_account.id; } asset_update_operation update_op; update_op.issuer = asset_to_update->issuer; update_op.asset_to_update = asset_to_update->id; update_op.new_issuer = new_issuer_account_id; update_op.new_options = new_options; signed_transaction tx; tx.operations.push_back( update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(new_options)(broadcast) ) } signed_transaction update_bitasset(string symbol, bitasset_options new_options, bool broadcast /* = false */) { try { optional asset_to_update = find_asset(symbol); if (!asset_to_update) FC_THROW("No asset with that symbol exists!"); asset_update_bitasset_operation update_op; update_op.issuer = asset_to_update->issuer; update_op.asset_to_update = asset_to_update->id; update_op.new_options = new_options; signed_transaction tx; tx.operations.push_back( update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (symbol)(new_options)(broadcast) ) } signed_transaction update_asset_feed_producers(string symbol, flat_set new_feed_producers, bool broadcast /* = false */) { try { optional asset_to_update = find_asset(symbol); if (!asset_to_update) FC_THROW("No asset with that symbol exists!"); asset_update_feed_producers_operation update_op; update_op.issuer = asset_to_update->issuer; update_op.asset_to_update = asset_to_update->id; update_op.new_feed_producers.reserve(new_feed_producers.size()); std::transform(new_feed_producers.begin(), new_feed_producers.end(), std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()), [this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); }); signed_transaction tx; tx.operations.push_back( update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (symbol)(new_feed_producers)(broadcast) ) } signed_transaction publish_asset_feed(string publishing_account, string symbol, price_feed feed, bool broadcast /* = false */) { try { optional asset_to_update = find_asset(symbol); if (!asset_to_update) FC_THROW("No asset with that symbol exists!"); asset_publish_feed_operation publish_op; publish_op.publisher = get_account_id(publishing_account); publish_op.asset_id = asset_to_update->id; publish_op.feed = feed; signed_transaction tx; tx.operations.push_back( publish_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) } signed_transaction fund_asset_fee_pool(string from, string symbol, string amount, bool broadcast /* = false */) { try { account_object from_account = get_account(from); optional asset_to_fund = find_asset(symbol); if (!asset_to_fund) FC_THROW("No asset with that symbol exists!"); asset_object core_asset = get_asset(asset_id_type()); asset_fund_fee_pool_operation fund_op; fund_op.from_account = from_account.id; fund_op.asset_id = asset_to_fund->id; fund_op.amount = core_asset.amount_from_string(amount).amount; signed_transaction tx; tx.operations.push_back( fund_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) } signed_transaction reserve_asset(string from, string amount, string symbol, bool broadcast /* = false */) { try { account_object from_account = get_account(from); optional asset_to_reserve = find_asset(symbol); if (!asset_to_reserve) FC_THROW("No asset with that symbol exists!"); asset_reserve_operation reserve_op; reserve_op.payer = from_account.id; reserve_op.amount_to_reserve = asset_to_reserve->amount_from_string(amount); signed_transaction tx; tx.operations.push_back( reserve_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) } signed_transaction global_settle_asset(string symbol, price settle_price, bool broadcast /* = false */) { try { optional asset_to_settle = find_asset(symbol); if (!asset_to_settle) FC_THROW("No asset with that symbol exists!"); asset_global_settle_operation settle_op; settle_op.issuer = asset_to_settle->issuer; settle_op.asset_to_settle = asset_to_settle->id; settle_op.settle_price = settle_price; signed_transaction tx; tx.operations.push_back( settle_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) } signed_transaction settle_asset(string account_to_settle, string amount_to_settle, string symbol, bool broadcast /* = false */) { try { optional asset_to_settle = find_asset(symbol); if (!asset_to_settle) FC_THROW("No asset with that symbol exists!"); asset_settle_operation settle_op; settle_op.account = get_account_id(account_to_settle); settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle); signed_transaction tx; tx.operations.push_back( settle_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) } signed_transaction whitelist_account(string authorizing_account, string account_to_list, account_whitelist_operation::account_listing new_listing_status, bool broadcast /* = false */) { try { account_whitelist_operation whitelist_op; whitelist_op.authorizing_account = get_account_id(authorizing_account); whitelist_op.account_to_list = get_account_id(account_to_list); whitelist_op.new_listing = new_listing_status; signed_transaction tx; tx.operations.push_back( whitelist_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } signed_transaction create_delegate(string owner_account, string url, bool broadcast /* = false */) { try { delegate_create_operation delegate_create_op; delegate_create_op.delegate_account = get_account_id(owner_account); delegate_create_op.url = url; if (_remote_db->get_delegate_by_account(delegate_create_op.delegate_account)) FC_THROW("Account ${owner_account} is already a delegate", ("owner_account", owner_account)); signed_transaction tx; tx.operations.push_back( delegate_create_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } witness_object get_witness(string owner_account) { try { fc::optional witness_id = maybe_id(owner_account); if (witness_id) { std::vector ids_to_get; ids_to_get.push_back(*witness_id); std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get); if (witness_objects.front()) return *witness_objects.front(); FC_THROW("No witness is registered for id ${id}", ("id", owner_account)); } else { // then maybe it's the owner account try { account_id_type owner_account_id = get_account_id(owner_account); fc::optional witness = _remote_db->get_witness_by_account(owner_account_id); if (witness) return *witness; else FC_THROW("No witness is registered for account ${account}", ("account", owner_account)); } catch (const fc::exception&) { FC_THROW("No account or witness named ${account}", ("account", owner_account)); } } } FC_CAPTURE_AND_RETHROW( (owner_account) ) } delegate_object get_delegate(string owner_account) { try { fc::optional delegate_id = maybe_id(owner_account); if (delegate_id) { std::vector ids_to_get; ids_to_get.push_back(*delegate_id); std::vector> delegate_objects = _remote_db->get_delegates(ids_to_get); if (delegate_objects.front()) return *delegate_objects.front(); FC_THROW("No delegate is registered for id ${id}", ("id", owner_account)); } else { // then maybe it's the owner account try { account_id_type owner_account_id = get_account_id(owner_account); fc::optional delegate = _remote_db->get_delegate_by_account(owner_account_id); if (delegate) return *delegate; else FC_THROW("No delegate is registered for account ${account}", ("account", owner_account)); } catch (const fc::exception&) { FC_THROW("No account or delegate named ${account}", ("account", owner_account)); } } } FC_CAPTURE_AND_RETHROW( (owner_account) ) } signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) { try { account_object witness_account = get_account(owner_account); fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account); int witness_key_index = find_first_unused_derived_key_index(active_private_key); fc::ecc::private_key witness_private_key = derive_private_key(key_to_wif(active_private_key), witness_key_index); graphene::chain::public_key_type witness_public_key = witness_private_key.get_public_key(); witness_create_operation witness_create_op; witness_create_op.witness_account = witness_account.id; witness_create_op.block_signing_key = witness_public_key; witness_create_op.url = url; secret_hash_type::encoder enc; fc::raw::pack(enc, witness_private_key); fc::raw::pack(enc, secret_hash_type()); witness_create_op.initial_secret = secret_hash_type::hash(enc.result()); if (_remote_db->get_witness_by_account(witness_create_op.witness_account)) FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account)); signed_transaction tx; tx.operations.push_back( witness_create_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); _wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } signed_transaction vote_for_delegate(string voting_account, string delegate, bool approve, bool broadcast /* = false */) { try { account_object voting_account_object = get_account(voting_account); account_id_type delegate_owner_account_id = get_account_id(delegate); fc::optional delegate_obj = _remote_db->get_delegate_by_account(delegate_owner_account_id); if (!delegate_obj) FC_THROW("Account ${delegate} is not registered as a delegate", ("delegate", delegate)); if (approve) { auto insert_result = voting_account_object.options.votes.insert(delegate_obj->vote_id); if (!insert_result.second) FC_THROW("Account ${account} was already voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate)); } else { unsigned votes_removed = voting_account_object.options.votes.erase(delegate_obj->vote_id); if (!votes_removed) FC_THROW("Account ${account} is already not voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate)); } account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; signed_transaction tx; tx.operations.push_back( account_update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(delegate)(approve)(broadcast) ) } signed_transaction vote_for_witness(string voting_account, string witness, bool approve, bool broadcast /* = false */) { try { account_object voting_account_object = get_account(voting_account); account_id_type witness_owner_account_id = get_account_id(witness); fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); if (approve) { auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); if (!insert_result.second) FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); } else { unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); if (!votes_removed) FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); } account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; signed_transaction tx; tx.operations.push_back( account_update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) } signed_transaction set_voting_proxy(string account_to_modify, optional voting_account, bool broadcast /* = false */) { try { account_object account_object_to_modify = get_account(account_to_modify); if (voting_account) { account_id_type new_voting_account_id = get_account_id(*voting_account); if (account_object_to_modify.options.voting_account == new_voting_account_id) FC_THROW("Voting proxy for ${account} is already set to ${voter}", ("account", account_to_modify)("voter", *voting_account)); account_object_to_modify.options.voting_account = new_voting_account_id; } else { if (account_object_to_modify.options.voting_account == account_id_type()) FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify)); account_object_to_modify.options.voting_account = account_id_type(); } account_update_operation account_update_op; account_update_op.account = account_object_to_modify.id; account_update_op.new_options = account_object_to_modify.options; signed_transaction tx; tx.operations.push_back( account_update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_modify)(voting_account)(broadcast) ) } signed_transaction set_desired_witness_and_delegate_count(string account_to_modify, uint16_t desired_number_of_witnesses, uint16_t desired_number_of_delegates, bool broadcast /* = false */) { try { account_object account_object_to_modify = get_account(account_to_modify); if (account_object_to_modify.options.num_witness == desired_number_of_witnesses && account_object_to_modify.options.num_committee == desired_number_of_delegates) FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${delegates} delegates", ("account", account_to_modify)("witnesses", desired_number_of_witnesses)("delegates",desired_number_of_witnesses)); account_object_to_modify.options.num_witness = desired_number_of_witnesses; account_object_to_modify.options.num_committee = desired_number_of_delegates; account_update_operation account_update_op; account_update_op.account = account_object_to_modify.id; account_update_op.new_options = account_object_to_modify.options; signed_transaction tx; tx.operations.push_back( account_update_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_modify)(desired_number_of_witnesses)(desired_number_of_delegates)(broadcast) ) } signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set req_active_approvals; flat_set req_owner_approvals; vector other_auths; tx.get_required_authorities( req_active_approvals, req_owner_approvals, other_auths ); for( const auto& auth : other_auths ) for( const auto& a : auth.account_auths ) req_active_approvals.insert(a.first); // std::merge lets us de-duplicate account_id's that occur in both // sets, and dump them into a vector (as required by remote_db api) // at the same time vector< account_id_type > v_approving_account_ids; std::merge(req_active_approvals.begin(), req_active_approvals.end(), req_owner_approvals.begin() , req_owner_approvals.end(), std::back_inserter(v_approving_account_ids)); /// TODO: fetch the accounts specified via other_auths as well. vector< optional > approving_account_objects = _remote_db->get_accounts( v_approving_account_ids ); /// TODO: recursively check one layer deeper in the authority tree for keys FC_ASSERT( approving_account_objects.size() == v_approving_account_ids.size() ); flat_map< account_id_type, account_object* > approving_account_lut; size_t i = 0; for( optional& approving_acct : approving_account_objects ) { if( !approving_acct.valid() ) { wlog( "operation_get_required_auths said approval of non-existing account ${id} was needed", ("id", v_approving_account_ids[i]) ); i++; continue; } approving_account_lut[ approving_acct->id ] = &(*approving_acct); i++; } flat_set< public_key_type > approving_key_set; for( account_id_type& acct_id : req_active_approvals ) { const auto it = approving_account_lut.find( acct_id ); if( it == approving_account_lut.end() ) continue; const account_object* acct = it->second; vector v_approving_keys = acct->active.get_keys(); for( const public_key_type& approving_key : v_approving_keys ) approving_key_set.insert( approving_key ); } for( account_id_type& acct_id : req_owner_approvals ) { const auto it = approving_account_lut.find( acct_id ); if( it == approving_account_lut.end() ) continue; const account_object* acct = it->second; vector v_approving_keys = acct->owner.get_keys(); for( const public_key_type& approving_key : v_approving_keys ) approving_key_set.insert( approving_key ); } for( const authority& a : other_auths ) { for( const auto& k : a.key_auths ) approving_key_set.insert( k.first ); } tx.set_expiration(get_dynamic_global_properties().head_block_id); for( public_key_type& key : approving_key_set ) { auto it = _keys.find(key); if( it != _keys.end() ) { fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); if( !privkey.valid() ) { FC_ASSERT( false, "Malformed private key in _keys" ); } tx.sign( *privkey ); } /// TODO: if transaction has enough signatures to be "valid" don't add any more, /// there are cases where the wallet may have more keys than strictly necessary and /// the transaction will be rejected if the transaction validates without requiring /// all signatures provided } if( broadcast ) _remote_net_broadcast->broadcast_transaction( tx ); return tx; } signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, string min_to_receive, string symbol_to_receive, uint32_t timeout_sec = 0, bool fill_or_kill = false, bool broadcast = false) { account_object seller = get_account( seller_account ); limit_order_create_operation op; op.seller = seller.id; op.amount_to_sell = get_asset(symbol_to_sell).amount_from_string(amount_to_sell); op.min_to_receive = get_asset(symbol_to_receive).amount_from_string(min_to_receive); if( timeout_sec ) op.expiration = fc::time_point::now() + fc::seconds(timeout_sec); op.fill_or_kill = fill_or_kill; signed_transaction tx; tx.operations.push_back(op); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction( tx, broadcast ); } signed_transaction borrow_asset(string seller_name, string amount_to_borrow, string asset_symbol, string amount_of_collateral, bool broadcast = false) { account_object seller = get_account(seller_name); asset_object mia = get_asset(asset_symbol); FC_ASSERT(mia.is_market_issued()); asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset); call_order_update_operation op; op.funding_account = seller.id; op.delta_debt = mia.amount_from_string(amount_to_borrow); op.delta_collateral = collateral.amount_from_string(amount_of_collateral); signed_transaction trx; trx.operations = {op}; set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); trx.validate(); idump((broadcast)); return sign_transaction(trx, broadcast); } signed_transaction cancel_order(object_id_type order_id, bool broadcast = false) { try { FC_ASSERT(!is_locked()); FC_ASSERT(order_id.space() == protocol_ids, "Invalid order ID ${id}", ("id", order_id)); signed_transaction trx; limit_order_cancel_operation op; op.fee_paying_account = get_object(order_id).seller; op.order = order_id; set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); trx.operations = {op}; trx.validate(); return sign_transaction(trx, broadcast); } FC_CAPTURE_AND_RETHROW((order_id)) } signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast = false) { try { FC_ASSERT( !self.is_locked() ); fc::optional asset_obj = get_asset(asset_symbol); FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); account_object from_account = get_account(from); account_object to_account = get_account(to); account_id_type from_id = from_account.id; account_id_type to_id = get_account_id(to); transfer_operation xfer_op; xfer_op.from = from_id; xfer_op.to = to_id; xfer_op.amount = asset_obj->amount_from_string(amount); if( memo.size() ) { xfer_op.memo = memo_data(); xfer_op.memo->from = from_account.options.memo_key; xfer_op.memo->to = to_account.options.memo_key; xfer_op.memo->set_message(get_private_key(from_account.options.memo_key), to_account.options.memo_key, memo); } signed_transaction tx; tx.operations.push_back(xfer_op); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } signed_transaction issue_asset(string to_account, string amount, string symbol, string memo, bool broadcast = false) { auto asset_obj = get_asset(symbol); account_object to = get_account(to_account); account_object issuer = get_account(asset_obj.issuer); asset_issue_operation issue_op; issue_op.issuer = asset_obj.issuer; issue_op.asset_to_issue = asset_obj.amount_from_string(amount); issue_op.issue_to_account = to.id; if( memo.size() ) { issue_op.memo = memo_data(); issue_op.memo->from = issuer.options.memo_key; issue_op.memo->to = to.options.memo_key; issue_op.memo->set_message(get_private_key(issuer.options.memo_key), to.options.memo_key, memo); } signed_transaction tx; tx.operations.push_back(issue_op); set_operation_fees(tx,_remote_db->get_global_properties().parameters.current_fees); tx.validate(); return sign_transaction(tx, broadcast); } std::map> get_result_formatters() const { std::map > m; m["help"] = [](variant result, const fc::variants& a) { return result.get_string(); }; m["gethelp"] = [](variant result, const fc::variants& a) { return result.get_string(); }; m["get_account_history"] = [this](variant result, const fc::variants& a) { auto r = result.as>(); std::stringstream ss; for( const operation_history_object& i : r ) { auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; i.op.visit(operation_printer(ss, *this, i.result)); ss << " \n"; } return ss.str(); }; m["list_account_balances"] = [this](variant result, const fc::variants& a) { auto r = result.as>(); vector asset_recs; std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { return get_asset(a.asset_id); }); std::stringstream ss; for( unsigned i = 0; i < asset_recs.size(); ++i ) ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; return ss.str(); }; return m; } void dbg_make_uia(string creator, string symbol) { asset_options opts; opts.flags &= ~(white_list | disable_force_settle | global_settle); opts.issuer_permissions = opts.flags; opts.core_exchange_rate = price(asset(1), asset(1,1)); create_asset(get_account(creator).name, symbol, 2, opts, {}, true); } void dbg_make_mia(string creator, string symbol) { asset_options opts; opts.flags &= ~white_list; opts.issuer_permissions = opts.flags; opts.core_exchange_rate = price(asset(1), asset(1,1)); bitasset_options bopts; create_asset(get_account(creator).name, symbol, 2, opts, bopts, true); } void flood_network(string prefix, uint32_t number_of_transactions) { const account_object& master = *_wallet.my_accounts.get().lower_bound("import"); int number_of_accounts = number_of_transactions / 3; number_of_transactions -= number_of_accounts; auto key = derive_private_key("floodshill", 0); try { dbg_make_uia(master.name, "SHILL"); } catch(...) {/* Ignore; the asset probably already exists.*/} fc::time_point start = fc::time_point::now(); for( int i = 0; i < number_of_accounts; ++i ) create_account_with_private_key(key, prefix + fc::to_string(i), master.name, master.name, true, false); fc::time_point end = fc::time_point::now(); ilog("Created ${n} accounts in ${time} milliseconds", ("n", number_of_accounts)("time", (end - start).count() / 1000)); start = fc::time_point::now(); for( int i = 0; i < number_of_accounts; ++i ) { transfer(master.name, prefix + fc::to_string(i), "10", "CORE", "", true); transfer(master.name, prefix + fc::to_string(i), "1", "CORE", "", true); } end = fc::time_point::now(); ilog("Transferred to ${n} accounts in ${time} milliseconds", ("n", number_of_accounts*2)("time", (end - start).count() / 1000)); start = fc::time_point::now(); for( int i = 0; i < number_of_accounts; ++i ) issue_asset(prefix + fc::to_string(i), "1000", "SHILL", "", true); end = fc::time_point::now(); ilog("Issued to ${n} accounts in ${time} milliseconds", ("n", number_of_accounts)("time", (end - start).count() / 1000)); } string _wallet_filename; wallet_data _wallet; map _keys; fc::sha512 _checksum; fc::api _remote_api; fc::api _remote_db; fc::api _remote_net_broadcast; fc::api _remote_hist; #ifdef __unix__ mode_t _old_umask; #endif const string _wallet_filename_extension = ".wallet"; mutable map _asset_cache; }; void operation_printer::fee(const asset& a)const { out << " (Fee: " << wallet.get_asset(a.asset_id).amount_to_pretty_string(a) << ")"; } void operation_printer::operator()(const transfer_operation& op) const { out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; if( op.memo ) { if( wallet.is_locked() ) { out << " -- Unlock wallet to see memo."; } else { try { FC_ASSERT(wallet._keys.count(op.memo->to), "Memo is encrypted to a key ${k} not in this wallet.", ("k", op.memo->to)); auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); out << " -- Memo: " << op.memo->get_message(*my_key, op.memo->from); } catch (const fc::exception& e) { out << " -- could not decrypt memo"; elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); } } } fee(op.fee); } void operation_printer::operator()(const account_create_operation& op) const { out << "Create Account '" << op.name << "'"; fee(op.fee); } void operation_printer::operator()(const account_update_operation& op) const { out << "Update Account '" << wallet.get_account(op.account).name << "'"; fee(op.fee); } void operation_printer::operator()(const asset_create_operation& op) const { out << "Create "; if( op.bitasset_opts.valid() ) out << "BitAsset "; else out << "User-Issue Asset "; out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name; fee(op.fee); } }}} namespace graphene { namespace wallet { wallet_api::wallet_api(fc::api rapi) : my(new detail::wallet_api_impl(*this, rapi)) { } wallet_api::~wallet_api() { } bool wallet_api::copy_wallet_file(string destination_filename) { return my->copy_wallet_file(destination_filename); } optional wallet_api::get_block(uint32_t num) { return my->_remote_db->get_block(num); } uint64_t wallet_api::get_account_count() const { return my->_remote_db->get_account_count(); } vector wallet_api::list_my_accounts() { return vector(my->_wallet.my_accounts.begin(), my->_wallet.my_accounts.end()); } map wallet_api::list_accounts(const string& lowerbound, uint32_t limit) { return my->_remote_db->lookup_accounts(lowerbound, limit); } vector wallet_api::list_account_balances(const string& id) { if( auto real_id = detail::maybe_id(id) ) return my->_remote_db->get_account_balances(*real_id, flat_set()); return my->_remote_db->get_account_balances(get_account(id).id, flat_set()); } vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const { return my->_remote_db->list_assets( lowerbound, limit ); } vector wallet_api::get_account_history(string name, int limit)const { return my->_remote_hist->get_account_history(get_account(name).get_id(), operation_history_id_type(), limit, operation_history_id_type()); } vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const { return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() ); } vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const { return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); } vector wallet_api::get_call_orders(string a, uint32_t limit)const { return my->_remote_db->get_call_orders(get_asset(a).id, limit); } vector wallet_api::get_settle_orders(string a, uint32_t limit)const { return my->_remote_db->get_settle_orders(get_asset(a).id, limit); } brain_key_info wallet_api::suggest_brain_key()const { brain_key_info result; // create a private key for secure entropy fc::sha256 sha_entropy1 = fc::ecc::private_key::generate().get_secret(); fc::sha256 sha_entropy2 = fc::ecc::private_key::generate().get_secret(); fc::bigint entropy1( sha_entropy1.data(), sha_entropy1.data_size() ); fc::bigint entropy2( sha_entropy2.data(), sha_entropy2.data_size() ); fc::bigint entropy(entropy1); entropy <<= 8*sha_entropy1.data_size(); entropy += entropy2; string brain_key = ""; for( int i=0; i 0 ) brain_key += " "; brain_key += graphene::words::word_list[ choice.to_int64() ]; } brain_key = normalize_brain_key(brain_key); fc::ecc::private_key priv_key = derive_private_key( brain_key, 0 ); result.brain_priv_key = brain_key; result.wif_priv_key = key_to_wif( priv_key ); result.pub_key = priv_key.get_public_key(); return result; } string wallet_api::serialize_transaction( signed_transaction tx )const { return fc::to_hex(fc::raw::pack(tx)); } variant wallet_api::get_object( object_id_type id ) const { return my->_remote_db->get_objects({id}); } string wallet_api::get_wallet_filename() const { return my->get_wallet_filename(); } transaction_handle_type wallet_api::begin_builder_transaction() { return my->begin_builder_transaction(); } void wallet_api::add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) { my->add_operation_to_builder_transaction(transaction_handle, op); } void wallet_api::replace_operation_in_builder_transaction(transaction_handle_type handle, unsigned operation_index, const operation& new_op) { my->replace_operation_in_builder_transaction(handle, operation_index, new_op); } asset wallet_api::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset) { return my->set_fees_on_builder_transaction(handle, fee_asset); } transaction wallet_api::preview_builder_transaction(transaction_handle_type handle) { return my->preview_builder_transaction(handle); } signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast) { return my->sign_builder_transaction(transaction_handle, broadcast); } signed_transaction wallet_api::propose_builder_transaction(transaction_handle_type handle, time_point_sec expiration, uint32_t review_period_seconds, bool broadcast) { return my->propose_builder_transaction(handle, expiration, review_period_seconds, broadcast); } void wallet_api::remove_builder_transaction(transaction_handle_type handle) { return my->remove_builder_transaction(handle); } account_object wallet_api::get_account(string account_name_or_id) const { return my->get_account(account_name_or_id); } asset_object wallet_api::get_asset(string asset_name_or_id) const { auto a = my->find_asset(asset_name_or_id); FC_ASSERT(a); return *a; } asset_bitasset_data_object wallet_api::get_bitasset_data(string asset_name_or_id) const { auto asset = get_asset(asset_name_or_id); FC_ASSERT(asset.is_market_issued() && asset.bitasset_data_id); return my->get_object(*asset.bitasset_data_id); } account_id_type wallet_api::get_account_id(string account_name_or_id) const { return my->get_account_id(account_name_or_id); } asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const { return my->get_asset_id(asset_symbol_or_id); } bool wallet_api::import_key(string account_name_or_id, string wif_key) { FC_ASSERT(!is_locked()); // backup wallet fc::optional optional_private_key = wif_to_key(wif_key); if (!optional_private_key) FC_THROW("Invalid private key ${key}", ("key", wif_key)); string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key()); copy_wallet_file( "before-import-key-" + shorthash ); if( my->import_key(account_name_or_id, wif_key) ) { save_wallet_file(); copy_wallet_file( "after-import-key-" + shorthash ); return true; } return false; } string wallet_api::normalize_brain_key(string s) const { return detail::normalize_brain_key( s ); } variant wallet_api::info() { return my->info(); } fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_string, int sequence_number) const { return detail::derive_private_key( prefix_string, sequence_number ); } signed_transaction wallet_api::register_account(string name, public_key_type owner_pubkey, public_key_type active_pubkey, string registrar_account, string referrer_account, uint8_t referrer_percent, bool broadcast) { return my->register_account( name, owner_pubkey, active_pubkey, registrar_account, referrer_account, referrer_percent, broadcast ); } signed_transaction wallet_api::create_account_with_brain_key(string brain_key, string account_name, string registrar_account, string referrer_account, bool broadcast /* = false */) { return my->create_account_with_brain_key( brain_key, account_name, registrar_account, referrer_account, broadcast ); } signed_transaction wallet_api::issue_asset(string to_account, string amount, string symbol, string memo, bool broadcast) { return my->issue_asset(to_account, amount, symbol, memo, broadcast); } signed_transaction wallet_api::transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast /* = false */) { return my->transfer(from, to, amount, asset_symbol, memo, broadcast); } signed_transaction wallet_api::create_asset(string issuer, string symbol, uint8_t precision, asset_options common, fc::optional bitasset_opts, bool broadcast) { return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast); } signed_transaction wallet_api::update_asset(string symbol, optional new_issuer, asset_options new_options, bool broadcast /* = false */) { return my->update_asset(symbol, new_issuer, new_options, broadcast); } signed_transaction wallet_api::update_bitasset(string symbol, bitasset_options new_options, bool broadcast /* = false */) { return my->update_bitasset(symbol, new_options, broadcast); } signed_transaction wallet_api::update_asset_feed_producers(string symbol, flat_set new_feed_producers, bool broadcast /* = false */) { return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast); } signed_transaction wallet_api::publish_asset_feed(string publishing_account, string symbol, price_feed feed, bool broadcast /* = false */) { return my->publish_asset_feed(publishing_account, symbol, feed, broadcast); } signed_transaction wallet_api::fund_asset_fee_pool(string from, string symbol, string amount, bool broadcast /* = false */) { return my->fund_asset_fee_pool(from, symbol, amount, broadcast); } signed_transaction wallet_api::reserve_asset(string from, string amount, string symbol, bool broadcast /* = false */) { return my->fund_asset_fee_pool(from, amount, symbol, broadcast); } signed_transaction wallet_api::global_settle_asset(string symbol, price settle_price, bool broadcast /* = false */) { return my->global_settle_asset(symbol, settle_price, broadcast); } signed_transaction wallet_api::settle_asset(string account_to_settle, string amount_to_settle, string symbol, bool broadcast /* = false */) { return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast); } signed_transaction wallet_api::whitelist_account(string authorizing_account, string account_to_list, account_whitelist_operation::account_listing new_listing_status, bool broadcast /* = false */) { return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } signed_transaction wallet_api::create_delegate(string owner_account, string url, bool broadcast /* = false */) { return my->create_delegate(owner_account, url, broadcast); } map wallet_api::list_witnesses(const string& lowerbound, uint32_t limit) { return my->_remote_db->lookup_witness_accounts(lowerbound, limit); } map wallet_api::list_delegates(const string& lowerbound, uint32_t limit) { return my->_remote_db->lookup_delegate_accounts(lowerbound, limit); } witness_object wallet_api::get_witness(string owner_account) { return my->get_witness(owner_account); } delegate_object wallet_api::get_delegate(string owner_account) { return my->get_delegate(owner_account); } signed_transaction wallet_api::create_witness(string owner_account, string url, bool broadcast /* = false */) { return my->create_witness(owner_account, url, broadcast); } signed_transaction wallet_api::vote_for_delegate(string voting_account, string witness, bool approve, bool broadcast /* = false */) { return my->vote_for_delegate(voting_account, witness, approve, broadcast); } signed_transaction wallet_api::vote_for_witness(string voting_account, string witness, bool approve, bool broadcast /* = false */) { return my->vote_for_witness(voting_account, witness, approve, broadcast); } signed_transaction wallet_api::set_voting_proxy(string account_to_modify, optional voting_account, bool broadcast /* = false */) { return my->set_voting_proxy(account_to_modify, voting_account, broadcast); } signed_transaction wallet_api::set_desired_witness_and_delegate_count(string account_to_modify, uint16_t desired_number_of_witnesses, uint16_t desired_number_of_delegates, bool broadcast /* = false */) { return my->set_desired_witness_and_delegate_count(account_to_modify, desired_number_of_witnesses, desired_number_of_delegates, broadcast); } void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = wallet_filename; } signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */) { try { return my->sign_transaction( tx, broadcast); } FC_CAPTURE_AND_RETHROW( (tx) ) } operation wallet_api::get_prototype_operation(string operation_name) { if (operation_name == "assert_operation") return graphene::chain::assert_operation(); if (operation_name == "balance_claim_operation") return graphene::chain::balance_claim_operation(); if (operation_name == "account_create_operation") return graphene::chain::account_create_operation(); if (operation_name == "account_whitelist_operation") return graphene::chain::account_whitelist_operation(); if (operation_name == "account_update_operation") return graphene::chain::account_update_operation(); if (operation_name == "account_upgrade_operation") return graphene::chain::account_upgrade_operation(); if (operation_name == "account_transfer_operation") return graphene::chain::account_transfer_operation(); if (operation_name == "delegate_create_operation") return graphene::chain::delegate_create_operation(); if (operation_name == "witness_create_operation") return graphene::chain::witness_create_operation(); if (operation_name == "witness_withdraw_pay_operation") return graphene::chain::witness_withdraw_pay_operation(); if (operation_name == "delegate_update_global_parameters_operation") return graphene::chain::delegate_update_global_parameters_operation(); if (operation_name == "transfer_operation") return graphene::chain::transfer_operation(); if (operation_name == "override_transfer_operation") return graphene::chain::override_transfer_operation(); if (operation_name == "asset_create_operation") return graphene::chain::asset_create_operation(); if (operation_name == "asset_global_settle_operation") return graphene::chain::asset_global_settle_operation(); if (operation_name == "asset_settle_operation") return graphene::chain::asset_settle_operation(); if (operation_name == "asset_fund_fee_pool_operation") return graphene::chain::asset_fund_fee_pool_operation(); if (operation_name == "asset_update_operation") return graphene::chain::asset_update_operation(); if (operation_name == "asset_update_bitasset_operation") return graphene::chain::asset_update_bitasset_operation(); if (operation_name == "asset_update_feed_producers_operation") return graphene::chain::asset_update_feed_producers_operation(); if (operation_name == "asset_publish_feed_operation") return graphene::chain::asset_publish_feed_operation(); if (operation_name == "asset_issue_operation") return graphene::chain::asset_issue_operation(); if (operation_name == "asset_reserve_operation") return graphene::chain::asset_reserve_operation(); if (operation_name == "limit_order_create_operation") return graphene::chain::limit_order_create_operation(); if (operation_name == "limit_order_cancel_operation") return graphene::chain::limit_order_cancel_operation(); if (operation_name == "call_order_update_operation") return graphene::chain::call_order_update_operation(); if (operation_name == "proposal_create_operation") return graphene::chain::proposal_create_operation(); if (operation_name == "proposal_update_operation") return graphene::chain::proposal_update_operation(); if (operation_name == "proposal_delete_operation") return graphene::chain::proposal_delete_operation(); if (operation_name == "fill_order_operation") return graphene::chain::fill_order_operation(); if (operation_name == "withdraw_permission_create_operation") return graphene::chain::withdraw_permission_create_operation(); if (operation_name == "withdraw_permission_update_operation") return graphene::chain::withdraw_permission_update_operation(); if (operation_name == "withdraw_permission_claim_operation") return graphene::chain::withdraw_permission_claim_operation(); if (operation_name == "withdraw_permission_delete_operation") return graphene::chain::withdraw_permission_delete_operation(); if (operation_name == "vesting_balance_create_operation") return graphene::chain::vesting_balance_create_operation(); if (operation_name == "vesting_balance_withdraw_operation") return graphene::chain::vesting_balance_withdraw_operation(); if (operation_name == "worker_create_operation") return graphene::chain::worker_create_operation(); if (operation_name == "custom_operation") return graphene::chain::custom_operation(); FC_THROW("Unsupported operation: \"${operation_name}\"", ("operation_name", operation_name)); } void wallet_api::dbg_make_uia(string creator, string symbol) { FC_ASSERT(!is_locked()); my->dbg_make_uia(creator, symbol); } void wallet_api::dbg_make_mia(string creator, string symbol) { FC_ASSERT(!is_locked()); my->dbg_make_mia(creator, symbol); } void wallet_api::flood_network(string prefix, uint32_t number_of_transactions) { FC_ASSERT(!is_locked()); my->flood_network(prefix, number_of_transactions); } global_property_object wallet_api::get_global_properties() const { return my->get_global_properties(); } dynamic_global_property_object wallet_api::get_dynamic_global_properties() const { return my->get_dynamic_global_properties(); } string wallet_api::help()const { std::vector method_names = my->method_documentation.get_method_names(); std::stringstream ss; for (const std::string method_name : method_names) { try { ss << my->method_documentation.get_brief_description(method_name); } catch (const fc::key_not_found_exception&) { ss << method_name << " (no help available)\n"; } } return ss.str(); } string wallet_api::gethelp(const string& method)const { fc::api tmp; std::stringstream ss; ss << "\n"; if( method == "import_key" ) { ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; } else if( method == "transfer" ) { ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n"; ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n"; ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n"; } else if( method == "create_account_with_brain_key" ) { ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n"; ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n"; ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n"; ss << "\n"; ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n"; ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n"; ss << "register_account if you already know the keys you know the public keys that you would like to register.\n"; } else if( method == "register_account" ) { ss << "usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR REFERRER REFERRER_PERCENT BROADCAST\n\n"; ss << "example: register_account \"newaccount\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"1.3.11\" \"1.3.11\" 50 true\n"; ss << "\n"; ss << "Use this method to register an account for which you do not know the private keys."; } else if( method == "create_asset" ) { ss << "usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\n\n"; ss << "PRECISION_DIGITS: the number of digits after the decimal point\n\n"; ss << "Example value of OPTIONS: \n"; ss << fc::json::to_pretty_string( graphene::chain::asset_options() ); ss << "\nExample value of BITASSET_OPTIONS: \n"; ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() ); ss << "\nBITASSET_OPTIONS may be null\n"; } else { std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); if (!doxygenHelpString.empty()) ss << doxygenHelpString; else ss << "No help defined for method " << method << "\n"; } return ss.str(); } bool wallet_api::load_wallet_file( string wallet_filename ) { return my->load_wallet_file( wallet_filename ); } void wallet_api::save_wallet_file( string wallet_filename ) { my->save_wallet_file( wallet_filename ); } std::map > wallet_api::get_result_formatters() const { return my->get_result_formatters(); } bool wallet_api::is_locked()const { return my->is_locked(); } bool wallet_api::is_new()const { return my->_wallet.cipher_keys.size() == 0; } void wallet_api::encrypt_keys() { my->encrypt_keys(); } void wallet_api::lock() { try { FC_ASSERT( !is_locked() ); encrypt_keys(); for( auto key : my->_keys ) key.second = key_to_wif({}); my->_keys.clear(); my->_checksum = fc::sha512(); my->self.lock_changed(true); } FC_CAPTURE_AND_RETHROW() } void wallet_api::unlock(string password) { try { FC_ASSERT(password.size() > 0); auto pw = fc::sha512::hash(password.c_str(), password.size()); vector decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys); auto pk = fc::raw::unpack(decrypted); FC_ASSERT(pk.checksum == pw); my->_keys = std::move(pk.keys); my->_checksum = pk.checksum; my->self.lock_changed(false); } FC_CAPTURE_AND_RETHROW() } void wallet_api::set_password( string password ) { if( !is_new() ) FC_ASSERT( !is_locked(), "The wallet must be unlocked before the password can be set" ); my->_checksum = fc::sha512::hash( password.c_str(), password.size() ); lock(); } signed_transaction wallet_api::import_balance( string name_or_id, const vector& wif_keys, bool broadcast ) { try { FC_ASSERT(!is_locked()); account_object claimer = get_account( name_or_id ); vector
addrs; map keys; for( auto wif_key : wif_keys ) { auto priv_key = wif_to_key(wif_key); FC_ASSERT( priv_key, "Invalid Private Key", ("key",wif_key) ); addrs.push_back( priv_key->get_public_key() ); keys[addrs.back()] = *priv_key; } auto balances = my->_remote_db->get_balance_objects( addrs ); wdump((balances)); addrs.clear(); set bal_types; for( auto b : balances ) bal_types.insert( b.balance.asset_id ); set
required_addrs; signed_transaction trx; for( auto a : bal_types ) { balance_claim_operation op; op.deposit_to_account = claimer.id; for( const auto& b : balances ) { if( b.balance.asset_id == a ) { op.total_claimed = b.vesting_policy.valid()? asset(0, b.balance.asset_id) : b.balance; op.balance_to_claim = b.id; op.balance_owner_key = keys[b.owner].get_public_key(); trx.operations.push_back(op); required_addrs.insert(b.owner); } } } my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); trx.validate(); auto tx = sign_transaction( trx, false ); for( auto a : required_addrs ) tx.sign( keys[a] ); // if the key for a balance object was the same as a key for the account we're importing it into, // we may end up with duplicate signatures, so remove those boost::erase(tx.signatures, boost::unique(boost::sort(tx.signatures))); if( broadcast ) my->_remote_net_broadcast->broadcast_transaction(tx); return tx; } FC_CAPTURE_AND_RETHROW( (name_or_id) ) } map wallet_api::dump_private_keys() { FC_ASSERT(!is_locked()); return my->_keys; } signed_transaction wallet_api::upgrade_account( string name, bool broadcast ) { return my->upgrade_account(name,broadcast); } signed_transaction wallet_api::sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, string min_to_receive, string symbol_to_receive, uint32_t expiration, bool fill_or_kill, bool broadcast) { return my->sell_asset(seller_account, amount_to_sell, symbol_to_sell, min_to_receive, symbol_to_receive, expiration, fill_or_kill, broadcast); } signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast) { FC_ASSERT(!is_locked()); return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); } } } void fc::to_variant(const account_multi_index_type& accts, fc::variant& vo) { vo = vector(accts.begin(), accts.end()); } void fc::from_variant(const fc::variant& var, account_multi_index_type& vo) { const vector& v = var.as>(); vo = account_multi_index_type(v.begin(), v.end()); }