BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
wallet_account.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/range/algorithm_ext/erase.hpp>
25 #include <boost/range/algorithm/unique.hpp>
26 #include <boost/range/algorithm/sort.hpp>
27 
28 #include "wallet_api_impl.hpp"
31 
32 /****
33  * Wallet API methods to handle accounts
34  */
35 
36 namespace graphene { namespace wallet { namespace detail {
37 
38  std::string wallet_api_impl::account_id_to_string(account_id_type id) const
39  {
40  std::string account_id = fc::to_string(id.space_id)
41  + "." + fc::to_string(id.type_id)
42  + "." + fc::to_string(id.instance.value);
43  return account_id;
44  }
45 
47  public_key_type active, string registrar_account, string referrer_account,
48  uint32_t referrer_percent, bool broadcast )
49  { try {
50  FC_ASSERT( !self.is_locked() );
51  FC_ASSERT( is_valid_name(name) );
52  account_create_operation account_create_op;
53 
54  // #449 referrer_percent is on 0-100 scale, if user has larger
55  // number it means their script is using GRAPHENE_100_PERCENT scale
56  // instead of 0-100 scale.
57  FC_ASSERT( referrer_percent <= 100 );
58  // TODO: process when pay_from_account is ID
59 
60  account_object registrar_account_object =
61  this->get_account( registrar_account );
62  FC_ASSERT( registrar_account_object.is_lifetime_member() );
63 
64  account_id_type registrar_account_id = registrar_account_object.id;
65 
66  account_object referrer_account_object =
67  this->get_account( referrer_account );
68  account_create_op.referrer = referrer_account_object.id;
69  account_create_op.referrer_percent = uint16_t( referrer_percent * GRAPHENE_1_PERCENT );
70 
71  account_create_op.registrar = registrar_account_id;
72  account_create_op.name = name;
73  account_create_op.owner = authority(1, owner, 1);
74  account_create_op.active = authority(1, active, 1);
75  account_create_op.options.memo_key = active;
76 
78  tx.operations.push_back( account_create_op );
79  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );
80  tx.validate();
81 
82  return sign_transaction(tx, broadcast);
83  } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)
84  (referrer_account)(referrer_percent)(broadcast) ) }
85 
87  { try {
88  FC_ASSERT( !self.is_locked() );
89  account_object account_obj = get_account(name);
90  FC_ASSERT( !account_obj.is_lifetime_member() );
91 
94  op.account_to_upgrade = account_obj.get_id();
96  tx.operations = {op};
97  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );
98  tx.validate();
99 
100  return sign_transaction( tx, broadcast );
101  } FC_CAPTURE_AND_RETHROW( (name) ) }
102 
104  string account_name,
105  string registrar_account,
106  string referrer_account,
107  bool broadcast,
108  bool save_wallet )
109  { try {
110  FC_ASSERT( !self.is_locked() );
111  string normalized_brain_key = normalize_brain_key( brain_key );
112  // TODO: scan blockchain for accounts that exist with same brain key
113  fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 );
114  return create_account_with_private_key(owner_privkey, account_name, registrar_account,
115  referrer_account, broadcast, save_wallet);
116  } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) }
117 
118  signed_transaction wallet_api_impl::account_store_map(string account, string catalog, bool remove,
119  flat_map<string, optional<string>> key_values, bool broadcast)
120  {
121  try
122  {
123  FC_ASSERT( !self.is_locked() );
124 
125  account_id_type account_id = get_account(account).id;
126 
127  custom_operation op;
128  account_storage_map store;
129  store.catalog = catalog;
130  store.remove = remove;
131  store.key_values = key_values;
132 
133  custom_plugin_operation custom_plugin_op(store);
134  auto packed = fc::raw::pack(custom_plugin_op);
135 
136  op.payer = account_id;
137  op.data = packed;
138 
140  tx.operations.push_back(op);
141  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());
142  tx.validate();
143 
144  return sign_transaction(tx, broadcast);
145 
146  } FC_CAPTURE_AND_RETHROW( (account)(remove)(catalog)(key_values)(broadcast) )
147  }
148 
149  void wallet_api_impl::claim_registered_account(const graphene::chain::account_object& account)
150  {
151  bool import_keys = false;
152  auto it = _wallet.pending_account_registrations.find( account.name );
154  for (const std::string& wif_key : it->second) {
155  if( !import_key( account.name, wif_key ) )
156  {
157  // somebody else beat our pending registration, there is
158  // nothing we can do except log it and move on
159  elog( "account ${name} registered by someone else first!",
160  ("name", account.name) );
161  // might as well remove it from pending regs,
162  // because there is now no way this registration
163  // can become valid (even in the extremely rare
164  // possibility of migrating to a fork where the
165  // name is available, the user can always
166  // manually re-register)
167  } else {
168  import_keys = true;
169  }
170  }
172 
173  if( import_keys ) {
175  }
176  }
177 
178  // after a witness registration succeeds, this saves the private key in the wallet permanently
179  //
180  void wallet_api_impl::claim_registered_witness(const std::string& witness_name)
181  {
182  auto iter = _wallet.pending_witness_registrations.find(witness_name);
184  std::string wif_key = iter->second;
185 
186  // get the list key id this key is registered with in the chain
187  fc::optional<fc::ecc::private_key> witness_private_key = wif_to_key(wif_key);
188  FC_ASSERT(witness_private_key);
189 
190  auto pub_key = witness_private_key->get_public_key();
191  _keys[pub_key] = wif_key;
193  }
194 
196  {
197  std::string account_id = account_id_to_string(id);
198 
199  auto rec = _remote_db->get_accounts({account_id}, {}).front();
200  FC_ASSERT(rec);
201  return *rec;
202  }
203 
204  account_object wallet_api_impl::get_account(string account_name_or_id) const
205  {
206  FC_ASSERT( account_name_or_id.size() > 0 );
207 
208  if( auto id = maybe_id<account_id_type>(account_name_or_id) )
209  {
210  // It's an ID
211  return get_account(*id);
212  } else {
213  auto rec = _remote_db->lookup_account_names({account_name_or_id}).front();
214  FC_ASSERT( rec && rec->name == account_name_or_id );
215  return *rec;
216  }
217  }
218 
219  account_id_type wallet_api_impl::get_account_id(string account_name_or_id) const
220  {
221  return get_account(account_name_or_id).get_id();
222  }
223 
225  string account_name, string registrar_account, string referrer_account, bool broadcast,
226  bool save_wallet )
227  { try {
228  int active_key_index = find_first_unused_derived_key_index(owner_privkey);
229  fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index);
230 
231  int memo_key_index = find_first_unused_derived_key_index(active_privkey);
232  fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index);
233 
234  graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key();
235  graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key();
236  graphene::chain::public_key_type memo_pubkey = memo_privkey.get_public_key();
237 
238  account_create_operation account_create_op;
239 
240  // TODO: process when pay_from_account is ID
241 
242  account_object registrar_account_object = get_account( registrar_account );
243 
244  account_id_type registrar_account_id = registrar_account_object.id;
245 
246  account_object referrer_account_object = get_account( referrer_account );
247  account_create_op.referrer = referrer_account_object.id;
248  account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage;
249 
250  account_create_op.registrar = registrar_account_id;
251  account_create_op.name = account_name;
252  account_create_op.owner = authority(1, owner_pubkey, 1);
253  account_create_op.active = authority(1, active_pubkey, 1);
254  account_create_op.options.memo_key = memo_pubkey;
255 
256  // current_fee_schedule()
257  // find_account(pay_from_account)
258 
259  // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule());
260 
262  tx.operations.push_back( account_create_op );
263  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());
264  tx.validate();
265 
266  // we do not insert owner_privkey here because
267  // it is intended to only be used for key recovery
268  _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey ));
269  _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey ));
270  if( save_wallet )
272  return sign_transaction(tx, broadcast);
273  } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) }
274 
275  signed_transaction wallet_api_impl::whitelist_account(string authorizing_account, string account_to_list,
276  account_whitelist_operation::account_listing new_listing_status, bool broadcast )
277  { try {
278  account_whitelist_operation whitelist_op;
279  whitelist_op.authorizing_account = get_account_id(authorizing_account);
280  whitelist_op.account_to_list = get_account_id(account_to_list);
281  whitelist_op.new_listing = new_listing_status;
282 
284  tx.operations.push_back( whitelist_op );
285  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());
286  tx.validate();
287 
288  return sign_transaction( tx, broadcast );
289  } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) }
290 
291  vector< vesting_balance_object_with_info > wallet_api_impl::get_vesting_balances( string account_name )
292  { try {
293  fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>( account_name );
294  std::vector<vesting_balance_object_with_info> result;
295  fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time;
296 
297  if( vbid )
298  {
299  result.emplace_back( get_object(*vbid), now );
300  return result;
301  }
302 
303  vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( account_name );
304  if( vbos.size() == 0 )
305  return result;
306 
307  for( const vesting_balance_object& vbo : vbos )
308  result.emplace_back( vbo, now );
309 
310  return result;
311  } FC_CAPTURE_AND_RETHROW( (account_name) )
312  }
313 
314  vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id,
315  const vector<string>& wif_keys, bool broadcast )
316  { try {
317  FC_ASSERT(!is_locked());
318  const dynamic_global_property_object& dpo = _remote_db->get_dynamic_global_properties();
319  account_object claimer = get_account( name_or_id );
320  uint32_t max_ops_per_tx = 30;
321 
322  map< address, private_key_type > keys; // local index of address -> private key
323  vector< address > addrs;
324  bool has_wildcard = false;
325  addrs.reserve( wif_keys.size() );
326  for( const string& wif_key : wif_keys )
327  {
328  if( wif_key == "*" )
329  {
330  if( has_wildcard )
331  continue;
332  for( const public_key_type& pub : _wallet.extra_keys[ claimer.id ] )
333  {
334  addrs.push_back( pub );
335  auto it = _keys.find( pub );
336  if( it != _keys.end() )
337  {
338  fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );
339  FC_ASSERT( privkey );
340  keys[ addrs.back() ] = *privkey;
341  }
342  else
343  {
344  wlog( "Somehow _keys has no private key for extra_keys public key ${k}", ("k", pub) );
345  }
346  }
347  has_wildcard = true;
348  }
349  else
350  {
351  optional< private_key_type > key = wif_to_key( wif_key );
352  FC_ASSERT( key.valid(), "Invalid private key" );
353  fc::ecc::public_key pk = key->get_public_key();
354  addrs.push_back( pk );
355  keys[addrs.back()] = *key;
356  // see chain/balance_evaluator.cpp
357  addrs.push_back( pts_address( pk, false, 56 ) );
358  keys[addrs.back()] = *key;
359  addrs.push_back( pts_address( pk, true, 56 ) );
360  keys[addrs.back()] = *key;
361  addrs.push_back( pts_address( pk, false, 0 ) );
362  keys[addrs.back()] = *key;
363  addrs.push_back( pts_address( pk, true, 0 ) );
364  keys[addrs.back()] = *key;
365  }
366  }
367 
368  vector< balance_object > balances = _remote_db->get_balance_objects( addrs );
369  addrs.clear();
370 
371  set<asset_id_type> bal_types;
372  for( auto b : balances ) bal_types.insert( b.balance.asset_id );
373 
374  struct claim_tx
375  {
376  vector< balance_claim_operation > ops;
377  set< address > addrs;
378  };
379  vector< claim_tx > claim_txs;
380 
381  for( const asset_id_type& a : bal_types )
382  {
384  op.deposit_to_account = claimer.id;
385  for( const balance_object& b : balances )
386  {
387  if( b.balance.asset_id == a )
388  {
389  op.total_claimed = b.available( dpo.time );
390  if( op.total_claimed.amount == 0 )
391  continue;
392  op.balance_to_claim = b.id;
393  op.balance_owner_key = keys[b.owner].get_public_key();
394  if( (claim_txs.empty()) || (claim_txs.back().ops.size() >= max_ops_per_tx) )
395  claim_txs.emplace_back();
396  claim_txs.back().ops.push_back(op);
397  claim_txs.back().addrs.insert(b.owner);
398  }
399  }
400  }
401 
402  vector< signed_transaction > result;
403 
404  for( const claim_tx& ctx : claim_txs )
405  {
407  tx.operations.reserve( ctx.ops.size() );
408  for( const balance_claim_operation& op : ctx.ops )
409  tx.operations.emplace_back( op );
410  set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );
411  tx.validate();
412  signed_transaction signed_tx = sign_transaction( tx, false );
413  for( const address& addr : ctx.addrs )
414  signed_tx.sign( keys[addr], _chain_id );
415  // if the key for a balance object was the same as a key for the account we're importing it into,
416  // we may end up with duplicate signatures, so remove those
417  boost::erase(signed_tx.signatures, boost::unique<boost::return_found_end>(boost::sort(signed_tx.signatures)));
418  result.push_back( signed_tx );
419  if( broadcast )
420  _remote_net_broadcast->broadcast_transaction(signed_tx);
421  }
422 
423  return result;
424  } FC_CAPTURE_AND_RETHROW( (name_or_id) ) }
425 
426  vector<flat_set<account_id_type>> wallet_api_impl::get_key_references(const vector<public_key_type> &keys) const
427  {
428  return _remote_db->get_key_references(keys);
429  }
430 
431 }}} // namespace graphene::wallet::detail
map< string, vector< string > > pending_account_registrations
bool import_key(string account_name_or_id, string wif_key)
account_id_type registrar
This account pays the fee. Must be a lifetime member.
Definition: account.hpp:100
bool upgrade_to_lifetime_member
If true, the account will be upgraded to a lifetime member; otherwise, it will add a year to the subs...
Definition: account.hpp:245
signed_transaction upgrade_account(string name, bool broadcast)
account_id_type get_account_id(string account_name_or_id) const
signed_transaction create_account_with_brain_key(string brain_key, string account_name, string registrar_account, string referrer_account, bool broadcast=false, bool save_wallet=true)
Identifies a weighted set of keys and accounts that must approve operations.
Definition: authority.hpp:34
Manage an account&#39;s membership statusThis operation is used to upgrade an account to a member...
Definition: account.hpp:234
void pack(Stream &s, const flat_set< T, A... > &value, uint32_t _max_depth)
Definition: flat.hpp:11
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)
vector< operation > operations
Definition: transaction.hpp:89
virtual void validate() const
Definition: transaction.cpp:58
This class represents an account on the object graphAccounts are the primary unit of authority on the...
Maintains global state information (committee_member list, current fees)This is an implementation det...
signed_transaction whitelist_account(string authorizing_account, string account_to_list, account_whitelist_operation::account_listing new_listing_status, bool broadcast)
Definition: api.cpp:56
#define elog(FORMAT,...)
Definition: logger.hpp:129
fc::optional< fc::ecc::private_key > wif_to_key(const std::string &wif_key)
vector< vesting_balance_object_with_info > get_vesting_balances(string account_name)
account_id_type account_to_upgrade
The account to upgrade; must not already be a lifetime member.
Definition: account.hpp:243
account_object get_account(account_id_type id) const
signed_transaction account_store_map(string account, string catalog, bool remove, flat_map< string, optional< string >> key_values, bool broadcast)
#define wlog(FORMAT,...)
Definition: logger.hpp:123
map< string, string > pending_witness_registrations
provides a generic way to add higher level protocols on top of witness consensusThere is no validatio...
Definition: custom.hpp:38
public_key get_public_key() const
fc::api< network_broadcast_api > _remote_net_broadcast
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
signed_transaction register_account(string name, public_key_type owner, public_key_type active, string registrar_account, string referrer_account, uint32_t referrer_percent, bool broadcast=false)
std::string key_to_wif(const fc::sha256 &private_secret)
Claim a balance in a balance_object.
Definition: balance.hpp:38
account_id_type authorizing_account
The account which is specifying an opinion of another account.
Definition: account.hpp:209
fc::ecc::private_key derive_private_key(const std::string &prefix_string, int sequence_number)
Definition: wallet_sign.cpp:54
object_id_type id
Definition: object.hpp:69
account_id_type get_id() const
contains only the public point of an elliptic curve key.
Definition: elliptic.hpp:35
vector< signed_transaction > import_balance(string name_or_id, const vector< string > &wif_keys, bool broadcast)
account_id_type referrer
This account receives a portion of the fee split between registrar and referrer. Must be a member...
Definition: account.hpp:103
signed_transaction sign_transaction(signed_transaction tx, bool broadcast=false)
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
string name
The account&#39;s name. This name must be unique among all account names on the graph. May not be empty.
adds a signature to a transaction
void save_wallet_file(string wallet_filename="")
string normalize_brain_key(string s)
Definition: wallet_sign.cpp:62
bool is_valid_name(const string &name)
Definition: account.cpp:60
vector< flat_set< account_id_type > > get_key_references(const vector< public_key_type > &keys) const
This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted ...
Definition: account.hpp:196
flat_map< string, optional< string > > key_values
std::string to_string(double)
Definition: string.cpp:73
graphene::db::object_downcast_t< ID > get_object(ID id) const
map< public_key_type, string > _keys
a 160 bit hash of a public key
Definition: address.hpp:44
an elliptic curve private key.
Definition: elliptic.hpp:89
map< account_id_type, set< public_key_type > > extra_keys
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
Definition: transaction.cpp:77
#define GRAPHENE_1_PERCENT
Definition: config.hpp:103
void set_operation_fees(signed_transaction &tx, const fee_schedule &s)
vector< signature_type > signatures
account_id_type account_to_list
The account being opined about.
Definition: account.hpp:211