2015-06-08 15:50:35 +00:00
/*
2015-06-19 23:26:29 +00:00
* Copyright ( c ) 2015 , Cryptonomex , Incn
2015-06-08 15:50:35 +00:00
* 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 <algorithm>
# include <cctype>
# include <iomanip>
# include <iostream>
# include <iterator>
# include <sstream>
# include <string>
# include <list>
2015-06-30 21:28:07 +00:00
# include <boost/range/adaptor/map.hpp>
2015-07-05 20:02:59 +00:00
# include <boost/range/algorithm_ext/erase.hpp>
# include <boost/range/algorithm/unique.hpp>
# include <boost/range/algorithm/sort.hpp>
2015-06-30 21:28:07 +00:00
2015-07-17 21:27:07 +00:00
# include <boost/multi_index_container.hpp>
# include <boost/multi_index/ordered_index.hpp>
# include <boost/multi_index/mem_fun.hpp>
# include <boost/multi_index/member.hpp>
# include <boost/multi_index/random_access_index.hpp>
# include <boost/multi_index/tag.hpp>
# include <boost/multi_index/sequenced_index.hpp>
# include <boost/multi_index/hashed_index.hpp>
2015-06-08 15:50:35 +00:00
# include <fc/io/fstream.hpp>
# include <fc/io/json.hpp>
# include <fc/io/stdio.hpp>
# include <fc/network/http/websocket.hpp>
# include <fc/rpc/cli.hpp>
# include <fc/rpc/websocket_api.hpp>
# include <fc/crypto/aes.hpp>
# include <fc/crypto/hex.hpp>
# include <fc/thread/mutex.hpp>
2015-06-30 21:28:07 +00:00
# include <fc/thread/scoped_lock.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/app/api.hpp>
# include <graphene/chain/asset_object.hpp>
# include <graphene/utilities/key_conversion.hpp>
2015-07-10 20:59:08 +00:00
# include <graphene/utilities/words.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/wallet/wallet.hpp>
2015-06-26 21:47:03 +00:00
# include <graphene/wallet/api_documentation.hpp>
2015-07-08 22:45:53 +00:00
# include <fc/smart_ref_impl.hpp>
2015-06-08 15:50:35 +00:00
# ifndef WIN32
2015-06-26 21:47:03 +00:00
# include <sys / types.h>
# include <sys / stat.h>
2015-06-08 15:50:35 +00:00
# endif
2015-07-10 20:59:08 +00:00
# define BRAIN_KEY_WORD_COUNT 16
2015-06-08 15:50:35 +00:00
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 ;
2015-07-24 21:02:06 +00:00
2015-06-08 15:50:35 +00:00
template < typename T >
2015-07-24 21:02:06 +00:00
void operator ( ) ( const T & op ) const ;
2015-06-08 15:50:35 +00:00
void operator ( ) ( const transfer_operation & op ) const ;
2015-07-24 21:02:06 +00:00
void operator ( ) ( const transfer_from_blind_operation & op ) const ;
void operator ( ) ( const transfer_to_blind_operation & op ) const ;
2015-06-08 15:50:35 +00:00
void operator ( ) ( const account_create_operation & op ) const ;
void operator ( ) ( const account_update_operation & op ) const ;
void operator ( ) ( const asset_create_operation & op ) const ;
} ;
2015-06-30 21:28:07 +00:00
template < class T >
optional < T > maybe_id ( const string & name_or_id )
2015-06-08 15:50:35 +00:00
{
if ( std : : isdigit ( name_or_id . front ( ) ) )
2015-06-30 21:28:07 +00:00
{
try
{
return fc : : variant ( name_or_id ) . as < T > ( ) ;
}
catch ( const fc : : exception & )
{
}
}
2015-06-08 15:50:35 +00:00
return optional < T > ( ) ;
}
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 ;
}
2015-06-30 21:28:07 +00:00
fc : : ecc : : private_key derive_private_key ( const std : : string & prefix_string ,
int sequence_number )
2015-06-08 15:50:35 +00:00
{
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 ;
}
2015-07-15 21:42:23 +00:00
struct op_prototype_visitor
{
typedef void result_type ;
int t = 0 ;
flat_map < std : : string , operation > & name2op ;
op_prototype_visitor (
int _t ,
flat_map < std : : string , operation > & _prototype_ops
) : t ( _t ) , name2op ( _prototype_ops ) { }
template < typename Type >
result_type operator ( ) ( const Type & op ) const
{
string name = fc : : get_typename < Type > : : name ( ) ;
size_t p = name . rfind ( ' : ' ) ;
if ( p ! = string : : npos )
name = name . substr ( p + 1 ) ;
name2op [ name ] = Type ( ) ;
}
} ;
2015-06-08 15:50:35 +00:00
class wallet_api_impl
{
2015-06-26 21:47:03 +00:00
public :
api_documentation method_documentation ;
private :
2015-06-08 15:50:35 +00:00
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 ( ) ) ;
2015-06-30 21:28:07 +00:00
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)
}
2015-06-08 15:50:35 +00:00
_wallet . pending_account_registrations . erase ( it ) ;
}
2015-06-30 21:28:07 +00:00
// 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 < fc : : ecc : : private_key > witness_private_key = wif_to_key ( wif_key ) ;
FC_ASSERT ( witness_private_key ) ;
2015-07-02 05:52:45 +00:00
auto pub_key = witness_private_key - > get_public_key ( ) ;
_keys [ pub_key ] = wif_key ;
2015-06-30 21:28:07 +00:00
_wallet . pending_witness_registrations . erase ( iter ) ;
}
2015-06-08 15:50:35 +00:00
fc : : mutex _resync_mutex ;
void resync ( )
{
2015-06-30 21:28:07 +00:00
fc : : scoped_lock < fc : : mutex > lock ( _resync_mutex ) ;
2015-06-08 15:50:35 +00:00
// 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
2015-06-30 21:28:07 +00:00
if ( ! _wallet . pending_account_registrations . empty ( ) )
2015-06-08 15:50:35 +00:00
{
2015-06-30 21:28:07 +00:00
// make a vector of the account names pending registration
std : : vector < string > pending_account_names = boost : : copy_range < std : : vector < string > > ( boost : : adaptors : : keys ( _wallet . pending_account_registrations ) ) ;
2015-06-08 15:50:35 +00:00
2015-06-30 21:28:07 +00:00
// look those up on the blockchain
std : : vector < fc : : optional < graphene : : chain : : account_object > >
pending_account_objects = _remote_db - > lookup_account_names ( pending_account_names ) ;
2015-06-08 15:50:35 +00:00
2015-06-30 21:28:07 +00:00
// if any of them exist, claim them
for ( const fc : : optional < graphene : : chain : : account_object > & 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 < string > pending_witness_names = boost : : copy_range < std : : vector < string > > ( boost : : adaptors : : keys ( _wallet . pending_witness_registrations ) ) ;
// look up the owners on the blockchain
std : : vector < fc : : optional < graphene : : chain : : account_object > > owner_account_objects = _remote_db - > lookup_account_names ( pending_witness_names ) ;
// if any of them have registered witnesses, claim them
for ( const fc : : optional < graphene : : chain : : account_object > & optional_account : owner_account_objects )
if ( optional_account )
{
fc : : optional < witness_object > witness_obj = _remote_db - > get_witness_by_account ( optional_account - > id ) ;
if ( witness_obj )
claim_registered_witness ( optional_account - > name ) ;
}
2015-06-08 15:50:35 +00:00
}
}
2015-07-01 21:47:16 +00:00
2015-06-08 15:50:35 +00:00
void enable_umask_protection ( )
{
# ifdef __unix__
_old_umask = umask ( S_IRWXG | S_IRWXO ) ;
# endif
}
2015-07-01 21:47:16 +00:00
2015-06-08 15:50:35 +00:00
void disable_umask_protection ( )
{
# ifdef __unix__
umask ( _old_umask ) ;
# endif
}
2015-07-15 21:42:23 +00:00
void init_prototype_ops ( )
{
operation op ;
for ( int t = 0 ; t < op . count ( ) ; t + + )
{
op . set_which ( t ) ;
op . visit ( op_prototype_visitor ( t , _prototype_ops ) ) ;
}
return ;
}
2015-06-08 15:50:35 +00:00
map < transaction_handle_type , signed_transaction > _builder_transactions ;
2015-07-17 21:27:07 +00:00
// if the user executes the same command twice in quick succession,
// we might generate the same transaction id, and cause the second
// transaction to be rejected. This can be avoided by altering the
// second transaction slightly (bumping up the expiration time by
// a second). Keep track of recent transaction ids we've generated
// so we can know if we need to do this
struct recently_generated_transaction_record
{
fc : : time_point_sec generation_time ;
graphene : : chain : : transaction_id_type transaction_id ;
} ;
struct timestamp_index { } ;
typedef boost : : multi_index_container < recently_generated_transaction_record ,
boost : : multi_index : : indexed_by < boost : : multi_index : : hashed_unique < boost : : multi_index : : member < recently_generated_transaction_record ,
graphene : : chain : : transaction_id_type ,
& recently_generated_transaction_record : : transaction_id > ,
std : : hash < graphene : : chain : : transaction_id_type > > ,
boost : : multi_index : : ordered_non_unique < boost : : multi_index : : tag < timestamp_index > ,
boost : : multi_index : : member < recently_generated_transaction_record , fc : : time_point_sec , & recently_generated_transaction_record : : generation_time > > > > recently_generated_transaction_set_type ;
recently_generated_transaction_set_type _recently_generated_transactions ;
2015-06-08 15:50:35 +00:00
public :
wallet_api & self ;
2015-08-24 19:08:09 +00:00
wallet_api_impl ( wallet_api & s , const wallet_data & initial_data , fc : : api < login_api > rapi )
2015-06-08 15:50:35 +00:00
: self ( s ) ,
2015-08-24 19:08:09 +00:00
_chain_id ( initial_data . chain_id ) ,
2015-06-08 15:50:35 +00:00
_remote_api ( rapi ) ,
_remote_db ( rapi - > database ( ) ) ,
2015-07-06 18:36:06 +00:00
_remote_net_broadcast ( rapi - > network_broadcast ( ) ) ,
2015-06-08 15:50:35 +00:00
_remote_hist ( rapi - > history ( ) )
{
2015-08-06 16:41:45 +00:00
chain_id_type remote_chain_id = _remote_db - > get_chain_id ( ) ;
if ( remote_chain_id ! = _chain_id )
{
FC_THROW ( " Remote server gave us an unexpected chain_id " ,
( " remote_chain_id " , remote_chain_id )
2015-08-24 19:08:09 +00:00
( " chain_id " , _chain_id ) ) ;
2015-08-06 16:41:45 +00:00
}
2015-07-15 21:42:23 +00:00
init_prototype_ops ( ) ;
2015-08-07 20:09:27 +00:00
_wallet . chain_id = _chain_id ;
2015-08-24 19:08:09 +00:00
_wallet . ws_server = initial_data . ws_server ;
_wallet . ws_user = initial_data . ws_user ;
_wallet . ws_password = initial_data . ws_password ;
2015-06-08 15:50:35 +00:00
}
virtual ~ wallet_api_impl ( )
{
2015-06-25 21:34:01 +00:00
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).
2015-06-30 20:42:41 +00:00
// If it has exited, cancel_all_subscriptsions() will throw and there's
2015-06-25 21:34:01 +00:00
// nothing we can do about it.
// dlog("Caught exception ${e} while canceling database subscriptions", ("e", e));
}
2015-06-08 15:50:35 +00:00
}
void encrypt_keys ( )
{
2015-07-24 21:02:06 +00:00
if ( ! is_locked ( ) )
{
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 ) ;
}
2015-06-08 15:50:35 +00:00
}
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 ) ;
2015-07-23 17:57:18 +00:00
disable_umask_protection ( ) ;
2015-06-08 15:50:35 +00:00
}
catch ( . . . )
{
disable_umask_protection ( ) ;
throw ;
}
return true ;
}
bool is_locked ( ) const
{
return _checksum = = fc : : sha512 ( ) ;
}
template < typename T >
T get_object ( object_id < T : : space_id , T : : type_id , T > id ) const
{
auto ob = _remote_db - > get_objects ( { id } ) . front ( ) ;
return ob . template as < T > ( ) ;
}
2015-07-08 22:45:53 +00:00
void set_operation_fees ( signed_transaction & tx , const fee_schedule & s )
{
for ( auto & op : tx . operations )
s . set_fee ( op ) ;
}
2015-06-08 15:50:35 +00:00
variant info ( ) const
{
2015-08-17 16:59:51 +00:00
auto chain_props = get_chain_properties ( ) ;
2015-06-08 15:50:35 +00:00
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 ) ;
2015-08-17 16:59:51 +00:00
result [ " chain_id " ] = chain_props . chain_id ;
2015-06-08 15:50:35 +00:00
result [ " active_witnesses " ] = global_props . active_witnesses ;
2015-07-13 20:06:02 +00:00
result [ " active_committee_members " ] = global_props . active_committee_members ;
2015-06-08 15:50:35 +00:00
return result ;
}
2015-08-17 16:59:51 +00:00
chain_property_object get_chain_properties ( ) const
{
return _remote_db - > get_chain_properties ( ) ;
}
2015-06-08 15:50:35 +00:00
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 < by_id > ( ) . count ( id ) )
return * _wallet . my_accounts . get < by_id > ( ) . 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_id_type > ( account_name_or_id ) )
{
// It's an ID
return get_account ( * id ) ;
} else {
// It's a name
if ( _wallet . my_accounts . get < by_name > ( ) . count ( account_name_or_id ) )
2015-07-17 21:27:07 +00:00
{
auto local_account = * _wallet . my_accounts . get < by_name > ( ) . find ( account_name_or_id ) ;
auto blockchain_account = _remote_db - > lookup_account_names ( { account_name_or_id } ) . front ( ) ;
FC_ASSERT ( blockchain_account ) ;
if ( local_account . id ! = blockchain_account - > id )
elog ( " my account id ${id} different from blockchain id ${id2} " , ( " id " , local_account . id ) ( " id2 " , blockchain_account - > id ) ) ;
if ( local_account . name ! = blockchain_account - > name )
elog ( " my account name ${id} different from blockchain name ${id2} " , ( " id " , local_account . name ) ( " id2 " , blockchain_account - > name ) ) ;
2015-06-08 15:50:35 +00:00
return * _wallet . my_accounts . get < by_name > ( ) . find ( account_name_or_id ) ;
2015-07-17 21:27:07 +00:00
}
2015-06-08 15:50:35 +00:00
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 < asset_object > find_asset ( asset_id_type id ) const
{
auto rec = _remote_db - > get_assets ( { id } ) . front ( ) ;
if ( rec )
_asset_cache [ id ] = * rec ;
return rec ;
}
optional < asset_object > find_asset ( string asset_symbol_or_id ) const
{
FC_ASSERT ( asset_symbol_or_id . size ( ) > 0 ) ;
if ( auto id = maybe_id < asset_id_type > ( 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_object > ( ) ;
_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 ;
}
2015-06-30 21:28:07 +00:00
2015-06-08 15:50:35 +00:00
asset_id_type get_asset_id ( string asset_symbol_or_id ) const
{
FC_ASSERT ( asset_symbol_or_id . size ( ) > 0 ) ;
vector < optional < asset_object > > opt_asset ;
if ( std : : isdigit ( asset_symbol_or_id . front ( ) ) )
return fc : : variant ( asset_symbol_or_id ) . as < asset_id_type > ( ) ;
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 ;
}
2015-06-30 21:28:07 +00:00
2015-06-08 15:50:35 +00:00
string get_wallet_filename ( ) const
{
return _wallet_filename ;
}
2015-06-30 21:28:07 +00:00
2015-07-02 05:52:45 +00:00
fc : : ecc : : private_key get_private_key ( const public_key_type & id ) const
2015-06-08 15:50:35 +00:00
{
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 ;
}
2015-06-30 21:28:07 +00:00
fc : : ecc : : private_key get_private_key_for_account ( const account_object & account ) const
{
2015-07-02 05:52:45 +00:00
vector < public_key_type > active_keys = account . active . get_keys ( ) ;
2015-06-30 21:28:07 +00:00
if ( active_keys . size ( ) ! = 1 )
FC_THROW ( " Expecting a simple authority with one active key " ) ;
return get_private_key ( active_keys . front ( ) ) ;
}
2015-07-03 00:05:31 +00:00
// 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)
2015-06-08 15:50:35 +00:00
bool import_key ( string account_name_or_id , string wif_key )
{
2015-07-03 00:05:31 +00:00
fc : : optional < fc : : ecc : : private_key > 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 ) ;
2015-06-08 15:50:35 +00:00
2015-07-03 00:05:31 +00:00
// make a list of all current public keys for the named account
flat_set < public_key_type > all_keys_for_account ;
2015-07-05 21:58:24 +00:00
std : : vector < public_key_type > active_keys = account . active . get_keys ( ) ;
std : : vector < public_key_type > 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 ( ) ) ) ;
2015-07-03 00:05:31 +00:00
all_keys_for_account . insert ( account . options . memo_key ) ;
_keys [ wif_pub_key ] = wif_key ;
2015-06-08 15:50:35 +00:00
2015-08-26 22:01:48 +00:00
_wallet . update_account ( account ) ;
2015-07-02 05:52:45 +00:00
2015-07-03 00:05:31 +00:00
_wallet . extra_keys [ account . id ] . insert ( wif_pub_key ) ;
2015-06-30 21:28:07 +00:00
2015-07-03 00:05:31 +00:00
return all_keys_for_account . find ( wif_pub_key ) ! = all_keys_for_account . end ( ) ;
2015-06-08 15:50:35 +00:00
}
2015-07-03 00:05:31 +00:00
2015-06-08 15:50:35 +00:00
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 ;
_wallet = fc : : json : : from_file ( wallet_filename ) . as < wallet_data > ( ) ;
2015-08-06 16:41:45 +00:00
if ( _wallet . chain_id ! = _chain_id )
FC_THROW ( " Wallet chain ID does not match " ,
( " wallet.chain_id " , _wallet . chain_id )
( " chain_id " , _chain_id ) ) ;
2015-06-08 15:50:35 +00:00
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 ( ) ;
2015-07-23 17:57:18 +00:00
disable_umask_protection ( ) ;
2015-06-08 15:50:35 +00:00
}
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 ,
2015-07-08 22:45:53 +00:00
uint32_t operation_index ,
2015-06-08 15:50:35 +00:00
const operation & new_op )
{
FC_ASSERT ( _builder_transactions . count ( handle ) ) ;
signed_transaction & trx = _builder_transactions [ handle ] ;
2015-07-08 22:45:53 +00:00
FC_ASSERT ( operation_index < trx . operations . size ( ) ) ;
2015-06-08 15:50:35 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
auto gprops = _remote_db - > get_global_properties ( ) . parameters ;
2015-06-08 15:50:35 +00:00
if ( fee_asset_obj . get_id ( ) ! = asset_id_type ( ) )
{
2015-07-08 22:45:53 +00:00
for ( auto & op : _builder_transactions [ handle ] . operations )
total_fee + = gprops . current_fees - > set_fee ( op , fee_asset_obj . options . core_exchange_rate ) ;
2015-06-08 15:50:35 +00:00
FC_ASSERT ( ( total_fee * fee_asset_obj . options . core_exchange_rate ) . amount < =
get_object < asset_dynamic_data_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 {
2015-07-08 22:45:53 +00:00
for ( auto & op : _builder_transactions [ handle ] . operations )
total_fee + = gprops . current_fees - > set_fee ( op ) ;
2015-06-08 15:50:35 +00:00
}
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 } ;
2015-07-08 22:45:53 +00:00
_remote_db - > get_global_properties ( ) . parameters . current_fees - > set_fee ( trx . operations . front ( ) ) ;
2015-06-08 15:50:35 +00:00
return trx = sign_transaction ( trx , broadcast ) ;
}
void remove_builder_transaction ( transaction_handle_type handle )
{
_builder_transactions . erase ( handle ) ;
}
2015-07-08 22:45:53 +00:00
2015-06-08 15:50:35 +00:00
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 ) ;
2015-06-09 20:46:00 +00:00
FC_ASSERT ( registrar_account_object . is_lifetime_member ( ) ) ;
2015-06-08 15:50:35 +00:00
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 ;
2015-07-02 05:52:45 +00:00
account_create_op . owner = authority ( 1 , owner , 1 ) ;
account_create_op . active = authority ( 1 , active , 1 ) ;
account_create_op . options . memo_key = active ;
2015-06-08 15:50:35 +00:00
signed_transaction tx ;
tx . operations . push_back ( account_create_op ) ;
2015-07-08 22:45:53 +00:00
auto current_fees = _remote_db - > get_global_properties ( ) . parameters . current_fees ;
set_operation_fees ( tx , current_fees ) ;
2015-06-08 15:50:35 +00:00
2015-07-02 05:52:45 +00:00
vector < public_key_type > paying_keys = registrar_account_object . active . get_keys ( ) ;
2015-06-08 15:50:35 +00:00
2015-07-14 22:46:58 +00:00
auto dyn_props = get_dynamic_global_properties ( ) ;
tx . set_reference_block ( dyn_props . head_block_id ) ;
tx . set_expiration ( dyn_props . time + fc : : seconds ( 30 ) ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
2015-07-02 05:52:45 +00:00
for ( public_key_type & key : paying_keys )
2015-06-08 15:50:35 +00:00
{
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 " ) ;
}
2015-08-06 16:41:45 +00:00
tx . sign ( * privkey , _chain_id ) ;
2015-06-08 15:50:35 +00:00
}
}
if ( broadcast )
2015-07-06 18:36:06 +00:00
_remote_net_broadcast - > broadcast_transaction ( tx ) ;
2015-06-08 15:50:35 +00:00
return tx ;
} FC_CAPTURE_AND_RETHROW ( ( name ) ( owner ) ( active ) ( registrar_account ) ( referrer_account ) ( referrer_percent ) ( broadcast ) ) }
2015-07-08 22:45:53 +00:00
2015-06-08 15:50:35 +00:00
signed_transaction upgrade_account ( string name , bool broadcast )
{ try {
FC_ASSERT ( ! self . is_locked ( ) ) ;
account_object account_obj = get_account ( name ) ;
2015-06-09 20:46:00 +00:00
FC_ASSERT ( ! account_obj . is_lifetime_member ( ) ) ;
2015-06-08 15:50:35 +00:00
signed_transaction tx ;
2015-06-10 18:17:13 +00:00
account_upgrade_operation op ;
op . account_to_upgrade = account_obj . get_id ( ) ;
op . upgrade_to_lifetime_member = true ;
tx . operations = { op } ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( name ) ) }
2015-06-30 21:28:07 +00:00
// 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 ( ) ;
2015-07-02 05:52:45 +00:00
if ( _keys . find ( derived_public_key ) = = _keys . end ( ) )
2015-06-30 21:28:07 +00:00
{
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 ;
}
}
}
2015-06-08 15:50:35 +00:00
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 {
2015-06-30 21:28:07 +00:00
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 ) ;
2015-06-08 15:50:35 +00:00
graphene : : chain : : public_key_type owner_pubkey = owner_privkey . get_public_key ( ) ;
graphene : : chain : : public_key_type active_pubkey = active_privkey . get_public_key ( ) ;
2015-06-30 21:28:07 +00:00
graphene : : chain : : public_key_type memo_pubkey = memo_privkey . get_public_key ( ) ;
2015-06-08 15:50:35 +00:00
account_create_operation account_create_op ;
// TODO: process when pay_from_account is ID
2015-06-30 21:28:07 +00:00
account_object registrar_account_object = get_account ( registrar_account ) ;
2015-06-08 15:50:35 +00:00
account_id_type registrar_account_id = registrar_account_object . id ;
2015-06-30 21:28:07 +00:00
account_object referrer_account_object = get_account ( referrer_account ) ;
2015-06-08 15:50:35 +00:00
account_create_op . referrer = referrer_account_object . id ;
2015-06-09 20:46:00 +00:00
account_create_op . referrer_percent = referrer_account_object . referrer_rewards_percentage ;
2015-06-08 15:50:35 +00:00
account_create_op . registrar = registrar_account_id ;
account_create_op . name = account_name ;
2015-07-02 05:52:45 +00:00
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 ;
2015-06-08 15:50:35 +00:00
// 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
2015-07-02 05:52:45 +00:00
vector < public_key_type > paying_keys = registrar_account_object . active . get_keys ( ) ;
2015-06-08 15:50:35 +00:00
2015-07-14 22:46:58 +00:00
auto dyn_props = get_dynamic_global_properties ( ) ;
tx . set_reference_block ( dyn_props . head_block_id ) ;
tx . set_expiration ( dyn_props . time + fc : : seconds ( 30 ) ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
2015-07-02 05:52:45 +00:00
for ( public_key_type & key : paying_keys )
2015-06-08 15:50:35 +00:00
{
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 " ) ;
2015-08-06 16:41:45 +00:00
tx . sign ( * privkey , _chain_id ) ;
2015-06-08 15:50:35 +00:00
}
}
// we do not insert owner_privkey here because
// it is intended to only be used for key recovery
2015-06-30 21:28:07 +00:00
_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 ) ) ;
2015-06-08 15:50:35 +00:00
if ( save_wallet )
save_wallet_file ( ) ;
if ( broadcast )
2015-07-06 18:36:06 +00:00
_remote_net_broadcast - > broadcast_transaction ( tx ) ;
2015-06-08 15:50:35 +00:00
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 ,
2015-07-17 21:27:07 +00:00
bool broadcast = false ,
bool save_wallet = true )
2015-06-08 15:50:35 +00:00
{ 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 ) ;
2015-07-17 21:27:07 +00:00
return create_account_with_private_key ( owner_privkey , account_name , registrar_account , referrer_account , broadcast , save_wallet ) ;
2015-06-08 15:50:35 +00:00
} FC_CAPTURE_AND_RETHROW ( ( account_name ) ( registrar_account ) ( referrer_account ) ) }
signed_transaction create_asset ( string issuer ,
string symbol ,
uint8_t precision ,
2015-07-08 22:45:53 +00:00
asset_options common ,
fc : : optional < bitasset_options > bitasset_opts ,
2015-06-08 15:50:35 +00:00
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 ;
2015-07-09 17:50:47 +00:00
create_op . bitasset_opts = bitasset_opts ;
2015-06-08 15:50:35 +00:00
signed_transaction tx ;
tx . operations . push_back ( create_op ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( issuer ) ( symbol ) ( precision ) ( common ) ( bitasset_opts ) ( broadcast ) ) }
2015-06-19 23:26:29 +00:00
signed_transaction update_asset ( string symbol ,
optional < string > new_issuer ,
2015-07-08 22:45:53 +00:00
asset_options new_options ,
2015-06-19 23:26:29 +00:00
bool broadcast /* = false */ )
{ try {
optional < asset_object > asset_to_update = find_asset ( symbol ) ;
if ( ! asset_to_update )
FC_THROW ( " No asset with that symbol exists! " ) ;
optional < account_id_type > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( symbol ) ( new_issuer ) ( new_options ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-19 23:26:29 +00:00
signed_transaction update_bitasset ( string symbol ,
2015-07-08 22:45:53 +00:00
bitasset_options new_options ,
2015-06-19 23:26:29 +00:00
bool broadcast /* = false */ )
{ try {
optional < asset_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( symbol ) ( new_options ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-19 23:26:29 +00:00
signed_transaction update_asset_feed_producers ( string symbol ,
flat_set < string > new_feed_producers ,
bool broadcast /* = false */ )
{ try {
optional < asset_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
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_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( publishing_account ) ( symbol ) ( feed ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-19 23:26:29 +00:00
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_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( from ) ( symbol ) ( amount ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-07-01 18:43:17 +00:00
signed_transaction reserve_asset ( string from ,
2015-06-19 23:26:29 +00:00
string amount ,
string symbol ,
bool broadcast /* = false */ )
{ try {
account_object from_account = get_account ( from ) ;
2015-07-01 18:43:17 +00:00
optional < asset_object > asset_to_reserve = find_asset ( symbol ) ;
if ( ! asset_to_reserve )
2015-06-19 23:26:29 +00:00
FC_THROW ( " No asset with that symbol exists! " ) ;
2015-07-01 18:43:17 +00:00
asset_reserve_operation reserve_op ;
reserve_op . payer = from_account . id ;
reserve_op . amount_to_reserve = asset_to_reserve - > amount_from_string ( amount ) ;
2015-06-19 23:26:29 +00:00
signed_transaction tx ;
2015-07-01 18:43:17 +00:00
tx . operations . push_back ( reserve_op ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( from ) ( amount ) ( symbol ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-19 23:26:29 +00:00
signed_transaction global_settle_asset ( string symbol ,
price settle_price ,
bool broadcast /* = false */ )
{ try {
optional < asset_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
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_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-19 23:26:29 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( account_to_settle ) ( amount_to_settle ) ( symbol ) ( broadcast ) ) }
2015-06-20 22:33:58 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-20 22:33:58 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( authorizing_account ) ( account_to_list ) ( new_listing_status ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-07-13 20:06:02 +00:00
signed_transaction create_committee_member ( string owner_account , string url ,
2015-06-22 23:37:18 +00:00
bool broadcast /* = false */ )
{ try {
2015-07-13 20:06:02 +00:00
committee_member_create_operation committee_member_create_op ;
committee_member_create_op . committee_member_account = get_account_id ( owner_account ) ;
committee_member_create_op . url = url ;
if ( _remote_db - > get_committee_member_by_account ( committee_member_create_op . committee_member_account ) )
FC_THROW ( " Account ${owner_account} is already a committee_member " , ( " owner_account " , owner_account ) ) ;
2015-06-22 23:37:18 +00:00
signed_transaction tx ;
2015-07-13 20:06:02 +00:00
tx . operations . push_back ( committee_member_create_op ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-22 23:37:18 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( owner_account ) ( broadcast ) ) }
2015-06-30 21:28:07 +00:00
witness_object get_witness ( string owner_account )
{
try
{
fc : : optional < witness_id_type > witness_id = maybe_id < witness_id_type > ( owner_account ) ;
if ( witness_id )
{
std : : vector < witness_id_type > ids_to_get ;
ids_to_get . push_back ( * witness_id ) ;
std : : vector < fc : : optional < witness_object > > 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_object > 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 ) )
}
2015-07-13 20:06:02 +00:00
committee_member_object get_committee_member ( string owner_account )
2015-07-01 21:47:16 +00:00
{
try
{
2015-07-13 20:06:02 +00:00
fc : : optional < committee_member_id_type > committee_member_id = maybe_id < committee_member_id_type > ( owner_account ) ;
if ( committee_member_id )
2015-07-01 21:47:16 +00:00
{
2015-07-13 20:06:02 +00:00
std : : vector < committee_member_id_type > ids_to_get ;
ids_to_get . push_back ( * committee_member_id ) ;
std : : vector < fc : : optional < committee_member_object > > committee_member_objects = _remote_db - > get_committee_members ( ids_to_get ) ;
if ( committee_member_objects . front ( ) )
return * committee_member_objects . front ( ) ;
FC_THROW ( " No committee_member is registered for id ${id} " , ( " id " , owner_account ) ) ;
2015-07-01 21:47:16 +00:00
}
else
{
// then maybe it's the owner account
try
{
account_id_type owner_account_id = get_account_id ( owner_account ) ;
2015-07-13 20:06:02 +00:00
fc : : optional < committee_member_object > committee_member = _remote_db - > get_committee_member_by_account ( owner_account_id ) ;
if ( committee_member )
return * committee_member ;
2015-07-01 21:47:16 +00:00
else
2015-07-13 20:06:02 +00:00
FC_THROW ( " No committee_member is registered for account ${account} " , ( " account " , owner_account ) ) ;
2015-07-01 21:47:16 +00:00
}
catch ( const fc : : exception & )
{
2015-07-13 20:06:02 +00:00
FC_THROW ( " No account or committee_member named ${account} " , ( " account " , owner_account ) ) ;
2015-07-01 21:47:16 +00:00
}
}
}
FC_CAPTURE_AND_RETHROW ( ( owner_account ) )
}
2015-06-22 23:37:18 +00:00
signed_transaction create_witness ( string owner_account ,
2015-06-30 21:28:07 +00:00
string url ,
2015-06-22 23:37:18 +00:00
bool broadcast /* = false */ )
{ try {
2015-06-30 21:28:07 +00:00
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 ( ) ;
2015-06-22 23:37:18 +00:00
witness_create_operation witness_create_op ;
2015-06-30 21:28:07 +00:00
witness_create_op . witness_account = witness_account . id ;
2015-07-02 05:52:45 +00:00
witness_create_op . block_signing_key = witness_public_key ;
2015-06-30 21:28:07 +00:00
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 ( ) ) ;
2015-06-22 23:37:18 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-22 23:37:18 +00:00
tx . validate ( ) ;
2015-06-30 21:28:07 +00:00
_wallet . pending_witness_registrations [ owner_account ] = key_to_wif ( witness_private_key ) ;
2015-06-22 23:37:18 +00:00
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( owner_account ) ( broadcast ) ) }
2015-07-13 20:06:02 +00:00
signed_transaction vote_for_committee_member ( string voting_account ,
string committee_member ,
2015-06-22 22:02:51 +00:00
bool approve ,
bool broadcast /* = false */ )
{ try {
account_object voting_account_object = get_account ( voting_account ) ;
2015-07-13 20:06:02 +00:00
account_id_type committee_member_owner_account_id = get_account_id ( committee_member ) ;
fc : : optional < committee_member_object > committee_member_obj = _remote_db - > get_committee_member_by_account ( committee_member_owner_account_id ) ;
if ( ! committee_member_obj )
FC_THROW ( " Account ${committee_member} is not registered as a committee_member " , ( " committee_member " , committee_member ) ) ;
2015-06-22 22:02:51 +00:00
if ( approve )
{
2015-07-13 20:06:02 +00:00
auto insert_result = voting_account_object . options . votes . insert ( committee_member_obj - > vote_id ) ;
2015-06-22 22:02:51 +00:00
if ( ! insert_result . second )
2015-07-13 20:06:02 +00:00
FC_THROW ( " Account ${account} was already voting for committee_member ${committee_member} " , ( " account " , voting_account ) ( " committee_member " , committee_member ) ) ;
2015-06-22 22:02:51 +00:00
}
else
{
2015-07-13 20:06:02 +00:00
unsigned votes_removed = voting_account_object . options . votes . erase ( committee_member_obj - > vote_id ) ;
2015-06-22 22:02:51 +00:00
if ( ! votes_removed )
2015-07-13 20:06:02 +00:00
FC_THROW ( " Account ${account} is already not voting for committee_member ${committee_member} " , ( " account " , voting_account ) ( " committee_member " , committee_member ) ) ;
2015-06-22 22:02:51 +00:00
}
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-22 22:02:51 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
2015-07-13 20:06:02 +00:00
} FC_CAPTURE_AND_RETHROW ( ( voting_account ) ( committee_member ) ( approve ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-22 22:02:51 +00:00
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_object > 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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-22 22:02:51 +00:00
tx . validate ( ) ;
2015-06-20 22:33:58 +00:00
2015-06-22 22:02:51 +00:00
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( voting_account ) ( witness ) ( approve ) ( broadcast ) ) }
2015-06-30 20:42:41 +00:00
2015-06-22 23:37:18 +00:00
signed_transaction set_voting_proxy ( string account_to_modify ,
optional < string > 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 ( ) ;
}
2015-06-30 20:42:41 +00:00
2015-06-22 23:37:18 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-22 23:37:18 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
} FC_CAPTURE_AND_RETHROW ( ( account_to_modify ) ( voting_account ) ( broadcast ) ) }
2015-07-13 20:06:02 +00:00
signed_transaction set_desired_witness_and_committee_member_count ( string account_to_modify ,
2015-07-06 19:27:42 +00:00
uint16_t desired_number_of_witnesses ,
2015-07-13 20:06:02 +00:00
uint16_t desired_number_of_committee_members ,
2015-07-06 19:27:42 +00:00
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 & &
2015-07-13 20:06:02 +00:00
account_object_to_modify . options . num_committee = = desired_number_of_committee_members )
FC_THROW ( " Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members " ,
( " account " , account_to_modify ) ( " witnesses " , desired_number_of_witnesses ) ( " committee_members " , desired_number_of_witnesses ) ) ;
2015-07-06 19:27:42 +00:00
account_object_to_modify . options . num_witness = desired_number_of_witnesses ;
2015-07-13 20:06:02 +00:00
account_object_to_modify . options . num_committee = desired_number_of_committee_members ;
2015-07-06 19:27:42 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-07-06 19:27:42 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
2015-07-13 20:06:02 +00:00
} FC_CAPTURE_AND_RETHROW ( ( account_to_modify ) ( desired_number_of_witnesses ) ( desired_number_of_committee_members ) ( broadcast ) ) }
2015-07-06 19:27:42 +00:00
2015-06-08 15:50:35 +00:00
signed_transaction sign_transaction ( signed_transaction tx , bool broadcast = false )
{
flat_set < account_id_type > req_active_approvals ;
flat_set < account_id_type > req_owner_approvals ;
2015-07-07 14:57:01 +00:00
vector < authority > other_auths ;
2015-06-08 15:50:35 +00:00
2015-07-07 14:57:01 +00:00
tx . get_required_authorities ( req_active_approvals , req_owner_approvals , other_auths ) ;
2015-06-08 15:50:35 +00:00
2015-07-07 14:57:01 +00:00
for ( const auto & auth : other_auths )
for ( const auto & a : auth . account_auths )
req_active_approvals . insert ( a . first ) ;
2015-06-08 15:50:35 +00:00
// 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
2015-07-17 21:27:07 +00:00
vector < account_id_type > v_approving_account_ids ;
2015-06-08 15:50:35 +00:00
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 ) ) ;
2015-07-07 14:57:01 +00:00
/// TODO: fetch the accounts specified via other_auths as well.
2015-06-08 15:50:35 +00:00
vector < optional < account_object > > approving_account_objects =
_remote_db - > get_accounts ( v_approving_account_ids ) ;
2015-07-07 14:57:01 +00:00
/// TODO: recursively check one layer deeper in the authority tree for keys
2015-06-08 15:50:35 +00:00
FC_ASSERT ( approving_account_objects . size ( ) = = v_approving_account_ids . size ( ) ) ;
2015-07-17 21:27:07 +00:00
flat_map < account_id_type , account_object * > approving_account_lut ;
2015-06-08 15:50:35 +00:00
size_t i = 0 ;
for ( optional < account_object > & 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 + + ;
}
2015-07-17 21:27:07 +00:00
flat_set < public_key_type > approving_key_set ;
2015-06-08 15:50:35 +00:00
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 ;
2015-07-02 05:52:45 +00:00
vector < public_key_type > v_approving_keys = acct - > active . get_keys ( ) ;
for ( const public_key_type & approving_key : v_approving_keys )
2015-06-08 15:50:35 +00:00
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 ;
2015-07-02 05:52:45 +00:00
vector < public_key_type > v_approving_keys = acct - > owner . get_keys ( ) ;
for ( const public_key_type & approving_key : v_approving_keys )
2015-06-08 15:50:35 +00:00
approving_key_set . insert ( approving_key ) ;
}
2015-07-07 14:57:01 +00:00
for ( const authority & a : other_auths )
{
for ( const auto & k : a . key_auths )
approving_key_set . insert ( k . first ) ;
}
2015-06-08 15:50:35 +00:00
2015-07-14 22:46:58 +00:00
auto dyn_props = get_dynamic_global_properties ( ) ;
tx . set_reference_block ( dyn_props . head_block_id ) ;
2015-06-08 15:50:35 +00:00
2015-07-17 21:27:07 +00:00
// first, some bookkeeping, expire old items from _recently_generated_transactions
// since transactions include the head block id, we just need the index for keeping transactions unique
// when there are multiple transactions in the same block. choose a time period that should be at
// least one block long, even in the worst case. 2 minutes ought to be plenty.
fc : : time_point_sec oldest_transaction_ids_to_track ( dyn_props . time - fc : : minutes ( 2 ) ) ;
auto oldest_transaction_record_iter = _recently_generated_transactions . get < timestamp_index > ( ) . lower_bound ( oldest_transaction_ids_to_track ) ;
auto begin_iter = _recently_generated_transactions . get < timestamp_index > ( ) . begin ( ) ;
_recently_generated_transactions . get < timestamp_index > ( ) . erase ( begin_iter , oldest_transaction_record_iter ) ;
uint32_t expiration_time_offset = 0 ;
for ( ; ; )
2015-06-08 15:50:35 +00:00
{
2015-07-17 21:27:07 +00:00
tx . set_expiration ( dyn_props . time + fc : : seconds ( 30 + expiration_time_offset ) ) ;
tx . signatures . clear ( ) ;
for ( public_key_type & key : approving_key_set )
2015-06-08 15:50:35 +00:00
{
2015-07-17 21:27:07 +00:00
auto it = _keys . find ( key ) ;
if ( it ! = _keys . end ( ) )
2015-06-08 15:50:35 +00:00
{
2015-07-17 21:27:07 +00:00
fc : : optional < fc : : ecc : : private_key > privkey = wif_to_key ( it - > second ) ;
FC_ASSERT ( privkey . valid ( ) , " Malformed private key in _keys " ) ;
2015-08-06 16:41:45 +00:00
tx . sign ( * privkey , _chain_id ) ;
2015-06-08 15:50:35 +00:00
}
2015-07-17 21:27:07 +00:00
/// 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
}
graphene : : chain : : transaction_id_type this_transaction_id = tx . id ( ) ;
auto iter = _recently_generated_transactions . find ( this_transaction_id ) ;
if ( iter = = _recently_generated_transactions . end ( ) )
{
// we haven't generated this transaction before, the usual case
recently_generated_transaction_record this_transaction_record ;
this_transaction_record . generation_time = dyn_props . time ;
this_transaction_record . transaction_id = this_transaction_id ;
_recently_generated_transactions . insert ( this_transaction_record ) ;
break ;
2015-06-08 15:50:35 +00:00
}
2015-07-17 21:27:07 +00:00
// else we've generated a dupe, increment expiration time and re-sign it
+ + expiration_time_offset ;
2015-06-08 15:50:35 +00:00
}
if ( broadcast )
2015-07-17 21:27:07 +00:00
{
try
{
_remote_net_broadcast - > broadcast_transaction ( tx ) ;
}
catch ( const fc : : exception & e )
{
elog ( " Caught exception while broadcasting transaction with id ${id} " , ( " id " , tx . id ( ) . str ( ) ) ) ;
throw ;
}
}
2015-06-08 15:50:35 +00:00
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 ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
}
2015-06-26 21:47:03 +00:00
signed_transaction borrow_asset ( string seller_name , string amount_to_borrow , string asset_symbol ,
2015-06-08 15:50:35 +00:00
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 ) ;
2015-06-16 15:22:28 +00:00
call_order_update_operation op ;
op . funding_account = seller . id ;
2015-06-26 21:47:03 +00:00
op . delta_debt = mia . amount_from_string ( amount_to_borrow ) ;
2015-06-18 19:17:48 +00:00
op . delta_collateral = collateral . amount_from_string ( amount_of_collateral ) ;
2015-06-08 15:50:35 +00:00
signed_transaction trx ;
trx . operations = { op } ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( trx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
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 ;
2015-06-16 15:22:28 +00:00
limit_order_cancel_operation op ;
op . fee_paying_account = get_object < limit_order_object > ( order_id ) . seller ;
op . order = order_id ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( trx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-16 15:22:28 +00:00
trx . operations = { op } ;
2015-06-08 15:50:35 +00:00
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_object > 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 ( ) ;
2015-06-16 18:42:02 +00:00
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 ) ,
2015-07-02 05:52:45 +00:00
to_account . options . memo_key , memo ) ;
2015-06-08 15:50:35 +00:00
}
signed_transaction tx ;
tx . operations . push_back ( xfer_op ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
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 ( ) ;
2015-06-16 18:42:02 +00:00
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 ) ,
2015-07-02 05:52:45 +00:00
to . options . memo_key , memo ) ;
2015-06-08 15:50:35 +00:00
}
signed_transaction tx ;
tx . operations . push_back ( issue_op ) ;
2015-07-08 22:45:53 +00:00
set_operation_fees ( tx , _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-08 15:50:35 +00:00
tx . validate ( ) ;
return sign_transaction ( tx , broadcast ) ;
}
std : : map < string , std : : function < string ( fc : : variant , const fc : : variants & ) > > get_result_formatters ( ) const
{
2015-06-26 21:47:03 +00:00
std : : map < string , std : : function < string ( fc : : variant , const fc : : variants & ) > > m ;
m [ " help " ] = [ ] ( variant result , const fc : : variants & a )
2015-06-08 15:50:35 +00:00
{
2015-06-26 21:47:03 +00:00
return result . get_string ( ) ;
} ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
m [ " gethelp " ] = [ ] ( variant result , const fc : : variants & a )
{
return result . get_string ( ) ;
} ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
m [ " get_account_history " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < vector < operation_history_object > > ( ) ;
std : : stringstream ss ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
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 " ;
}
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
return ss . str ( ) ;
} ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
m [ " list_account_balances " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < vector < asset > > ( ) ;
vector < asset_object > asset_recs ;
std : : transform ( r . begin ( ) , r . end ( ) , std : : back_inserter ( asset_recs ) , [ this ] ( const asset & a ) {
return get_asset ( a . asset_id ) ;
} ) ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
std : : stringstream ss ;
for ( unsigned i = 0 ; i < asset_recs . size ( ) ; + + i )
ss < < asset_recs [ i ] . amount_to_pretty_string ( r [ i ] ) < < " \n " ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
return ss . str ( ) ;
} ;
2015-06-08 15:50:35 +00:00
2015-07-24 21:02:06 +00:00
m [ " get_blind_balances " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < vector < asset > > ( ) ;
vector < asset_object > 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 ( ) ;
} ;
m [ " transfer_to_blind " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < blind_confirmation > ( ) ;
std : : stringstream ss ;
r . trx . operations [ 0 ] . visit ( operation_printer ( ss , * this , operation_result ( ) ) ) ;
ss < < " \n " ;
for ( const auto & out : r . outputs )
{
asset_object a = get_asset ( out . decrypted_memo . amount . asset_id ) ;
ss < < a . amount_to_pretty_string ( out . decrypted_memo . amount ) < < " to " < < out . label < < " \n \t receipt: " < < out . confirmation_receipt < < " \n \n " ;
}
return ss . str ( ) ;
} ;
m [ " blind_transfer " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < blind_confirmation > ( ) ;
std : : stringstream ss ;
r . trx . operations [ 0 ] . visit ( operation_printer ( ss , * this , operation_result ( ) ) ) ;
ss < < " \n " ;
for ( const auto & out : r . outputs )
{
asset_object a = get_asset ( out . decrypted_memo . amount . asset_id ) ;
ss < < a . amount_to_pretty_string ( out . decrypted_memo . amount ) < < " to " < < out . label < < " \n \t receipt: " < < out . confirmation_receipt < < " \n \n " ;
}
return ss . str ( ) ;
} ;
m [ " receive_blind_transfer " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto r = result . as < blind_receipt > ( ) ;
std : : stringstream ss ;
asset_object as = get_asset ( r . amount . asset_id ) ;
ss < < as . amount_to_pretty_string ( r . amount ) < < " " < < r . from_label < < " => " < < r . to_label < < " " < < r . memo < < " \n " ;
return ss . str ( ) ;
} ;
m [ " blind_history " ] = [ this ] ( variant result , const fc : : variants & a )
{
auto records = result . as < vector < blind_receipt > > ( ) ;
std : : stringstream ss ;
ss < < " WHEN "
< < " " < < " AMOUNT " < < " " < < " FROM " < < " => " < < " TO " < < " " < < " MEMO " < < " \n " ;
ss < < " ==================================================================================== \n " ;
for ( auto & r : records )
{
asset_object as = get_asset ( r . amount . asset_id ) ;
ss < < fc : : get_approximate_relative_time_string ( r . date )
< < " " < < as . amount_to_pretty_string ( r . amount ) < < " " < < r . from_label < < " => " < < r . to_label < < " " < < r . memo < < " \n " ;
}
return ss . str ( ) ;
} ;
2015-06-26 21:47:03 +00:00
return m ;
}
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
void dbg_make_uia ( string creator , string symbol )
{
2015-07-08 22:45:53 +00:00
asset_options opts ;
2015-06-26 21:47:03 +00:00
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 ) ;
}
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
void dbg_make_mia ( string creator , string symbol )
2015-06-08 15:50:35 +00:00
{
2015-07-08 22:45:53 +00:00
asset_options opts ;
2015-06-26 21:47:03 +00:00
opts . flags & = ~ white_list ;
opts . issuer_permissions = opts . flags ;
opts . core_exchange_rate = price ( asset ( 1 ) , asset ( 1 , 1 ) ) ;
2015-07-08 22:45:53 +00:00
bitasset_options bopts ;
2015-06-26 21:47:03 +00:00
create_asset ( get_account ( creator ) . name , symbol , 2 , opts , bopts , true ) ;
2015-06-08 15:50:35 +00:00
}
2015-06-26 21:47:03 +00:00
void flood_network ( string prefix , uint32_t number_of_transactions )
{
2015-07-17 21:27:07 +00:00
try
2015-06-26 21:47:03 +00:00
{
2015-07-17 21:27:07 +00:00
const account_object & master = * _wallet . my_accounts . get < by_name > ( ) . 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 )
{
std : : ostringstream brain_key ;
brain_key < < " brain key for account " < < prefix < < i ;
signed_transaction trx = create_account_with_brain_key ( brain_key . str ( ) , prefix + fc : : to_string ( i ) , master . name , master . name , /* broadcast = */ true , /* save wallet = */ 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 )
{
signed_transaction trx = transfer ( master . name , prefix + fc : : to_string ( i ) , " 10 " , " CORE " , " " , true ) ;
trx = 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 )
{
signed_transaction trx = 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 ) ) ;
}
catch ( . . . )
{
throw ;
2015-06-26 21:47:03 +00:00
}
}
2015-06-08 15:50:35 +00:00
2015-07-15 21:42:23 +00:00
operation get_prototype_operation ( string operation_name )
{
auto it = _prototype_ops . find ( operation_name ) ;
if ( it = = _prototype_ops . end ( ) )
FC_THROW ( " Unsupported operation: \" ${operation_name} \" " , ( " operation_name " , operation_name ) ) ;
return it - > second ;
}
2015-06-26 21:47:03 +00:00
string _wallet_filename ;
wallet_data _wallet ;
2015-06-08 15:50:35 +00:00
2015-07-02 05:52:45 +00:00
map < public_key_type , string > _keys ;
fc : : sha512 _checksum ;
2015-06-08 15:50:35 +00:00
2015-08-06 16:41:45 +00:00
chain_id_type _chain_id ;
2015-06-26 21:47:03 +00:00
fc : : api < login_api > _remote_api ;
fc : : api < database_api > _remote_db ;
2015-07-06 18:36:06 +00:00
fc : : api < network_broadcast_api > _remote_net_broadcast ;
2015-06-26 21:47:03 +00:00
fc : : api < history_api > _remote_hist ;
2015-06-08 15:50:35 +00:00
2015-07-15 21:42:23 +00:00
flat_map < string , operation > _prototype_ops ;
2015-06-08 15:50:35 +00:00
# ifdef __unix__
2015-06-26 21:47:03 +00:00
mode_t _old_umask ;
2015-06-08 15:50:35 +00:00
# endif
2015-06-26 21:47:03 +00:00
const string _wallet_filename_extension = " .wallet " ;
2015-06-08 15:50:35 +00:00
2015-06-26 21:47:03 +00:00
mutable map < asset_id_type , asset_object > _asset_cache ;
2015-06-08 15:50:35 +00:00
} ;
void operation_printer : : fee ( const asset & a ) const {
out < < " (Fee: " < < wallet . get_asset ( a . asset_id ) . amount_to_pretty_string ( a ) < < " ) " ;
}
2015-07-24 21:02:06 +00:00
template < typename T >
void operation_printer : : operator ( ) ( const T & op ) const
{
//balance_accumulator acc;
//op.get_balance_delta( acc, result );
auto a = wallet . get_asset ( op . fee . asset_id ) ;
auto payer = wallet . get_account ( op . fee_payer ( ) ) ;
string op_name = fc : : get_typename < T > : : 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 < < payer . name < < " fee: " < < a . amount_to_pretty_string ( op . fee ) ;
}
void operation_printer : : operator ( ) ( const transfer_from_blind_operation & op ) const
{
auto a = wallet . get_asset ( op . fee . asset_id ) ;
auto receiver = wallet . get_account ( op . to ) ;
out < < receiver . name
< < " received " < < a . amount_to_pretty_string ( op . amount ) < < " from blinded balance " ;
}
void operation_printer : : operator ( ) ( const transfer_to_blind_operation & op ) const
{
auto fa = wallet . get_asset ( op . fee . asset_id ) ;
auto a = wallet . get_asset ( op . amount . asset_id ) ;
auto sender = wallet . get_account ( op . from ) ;
out < < sender . name
< < " sent " < < a . amount_to_pretty_string ( op . amount ) < < " to " < < op . outputs . size ( ) < < " blinded balance " < < ( op . outputs . size ( ) > 1 ? " s " : " " )
< < " fee: " < < fa . amount_to_pretty_string ( op . fee ) ;
}
2015-06-08 15:50:35 +00:00
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. " ) ;
2015-07-02 05:52:45 +00:00
out < < " -- Memo: " < < op . memo - > get_message ( * my_key , op . memo - > from ) ;
2015-06-08 15:50:35 +00:00
} 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 " ;
2015-07-09 17:50:47 +00:00
if ( op . bitasset_opts . valid ( ) )
2015-06-08 15:50:35 +00:00
out < < " BitAsset " ;
else
out < < " User-Issue Asset " ;
out < < " ' " < < op . symbol < < " ' with issuer " < < wallet . get_account ( op . issuer ) . name ;
fee ( op . fee ) ;
}
} } }
2015-07-23 22:46:06 +00:00
2015-06-08 15:50:35 +00:00
namespace graphene { namespace wallet {
2015-08-24 19:08:09 +00:00
wallet_api : : wallet_api ( const wallet_data & initial_data , fc : : api < login_api > rapi )
: my ( new detail : : wallet_api_impl ( * this , initial_data , rapi ) )
2015-06-08 15:50:35 +00:00
{
}
wallet_api : : ~ wallet_api ( )
{
}
bool wallet_api : : copy_wallet_file ( string destination_filename )
{
return my - > copy_wallet_file ( destination_filename ) ;
}
optional < signed_block > 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 < account_object > wallet_api : : list_my_accounts ( )
{
return vector < account_object > ( my - > _wallet . my_accounts . begin ( ) , my - > _wallet . my_accounts . end ( ) ) ;
}
map < string , account_id_type > wallet_api : : list_accounts ( const string & lowerbound , uint32_t limit )
{
return my - > _remote_db - > lookup_accounts ( lowerbound , limit ) ;
}
vector < asset > wallet_api : : list_account_balances ( const string & id )
{
if ( auto real_id = detail : : maybe_id < account_id_type > ( id ) )
return my - > _remote_db - > get_account_balances ( * real_id , flat_set < asset_id_type > ( ) ) ;
return my - > _remote_db - > get_account_balances ( get_account ( id ) . id , flat_set < asset_id_type > ( ) ) ;
}
vector < asset_object > wallet_api : : list_assets ( const string & lowerbound , uint32_t limit ) const
{
return my - > _remote_db - > list_assets ( lowerbound , limit ) ;
}
vector < operation_history_object > 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 ( ) ) ;
}
2015-06-25 13:38:31 +00:00
vector < bucket_object > 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 ( ) ) ;
}
2015-06-08 15:50:35 +00:00
vector < limit_order_object > 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 < call_order_object > wallet_api : : get_call_orders ( string a , uint32_t limit ) const
{
return my - > _remote_db - > get_call_orders ( get_asset ( a ) . id , limit ) ;
}
vector < force_settlement_object > wallet_api : : get_settle_orders ( string a , uint32_t limit ) const
{
return my - > _remote_db - > get_settle_orders ( get_asset ( a ) . id , limit ) ;
}
2015-07-10 20:59:08 +00:00
brain_key_info wallet_api : : suggest_brain_key ( ) const
2015-06-08 15:50:35 +00:00
{
2015-07-10 20:59:08 +00:00
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 < BRAIN_KEY_WORD_COUNT ; i + + )
{
fc : : bigint choice = entropy % graphene : : words : : word_list_size ;
entropy / = graphene : : words : : word_list_size ;
if ( 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 ;
2015-06-08 15:50:35 +00:00
}
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 } ) ;
}
2015-06-26 21:47:03 +00:00
string wallet_api : : get_wallet_filename ( ) const
{
return my - > get_wallet_filename ( ) ;
}
2015-06-08 15:50:35 +00:00
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 ) ;
}
2015-06-30 21:28:07 +00:00
void wallet_api : : replace_operation_in_builder_transaction ( transaction_handle_type handle , unsigned operation_index , const operation & new_op )
2015-06-08 15:50:35 +00:00
{
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_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
2015-07-06 20:05:22 +00:00
fc : : optional < fc : : ecc : : private_key > 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 ( ) ) ;
2015-06-08 15:50:35 +00:00
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 ;
}
2015-08-14 20:51:04 +00:00
map < string , bool > wallet_api : : import_accounts ( string filename , string password )
{
FC_ASSERT ( ! is_locked ( ) ) ;
FC_ASSERT ( fc : : exists ( filename ) ) ;
const auto imported_keys = fc : : json : : from_file < exported_keys > ( filename ) ;
const auto password_hash = fc : : sha512 : : hash ( password ) ;
FC_ASSERT ( fc : : sha512 : : hash ( password_hash ) = = imported_keys . password_checksum ) ;
map < string , bool > result ;
for ( const auto & item : imported_keys . account_keys )
{
const auto import_this_account = [ & ] ( ) - > bool
{
try
{
const account_object account = get_account ( item . account_name ) ;
const auto & owner_keys = account . owner . get_keys ( ) ;
const auto & active_keys = account . active . get_keys ( ) ;
for ( const auto & public_key : item . public_keys )
{
if ( std : : find ( owner_keys . begin ( ) , owner_keys . end ( ) , public_key ) ! = owner_keys . end ( ) )
return true ;
if ( std : : find ( active_keys . begin ( ) , active_keys . end ( ) , public_key ) ! = active_keys . end ( ) )
return true ;
}
}
catch ( . . . )
{
}
return false ;
} ;
const auto should_proceed = import_this_account ( ) ;
result [ item . account_name ] = should_proceed ;
if ( should_proceed )
{
// TODO: First check that all private keys match public keys
for ( const auto & encrypted_key : item . encrypted_private_keys )
{
const auto plain_text = fc : : aes_decrypt ( password_hash , encrypted_key ) ;
const auto private_key = fc : : raw : : unpack < private_key_type > ( plain_text ) ;
import_key ( item . account_name , string ( graphene : : utilities : : key_to_wif ( private_key ) ) ) ;
}
}
}
return result ;
}
bool wallet_api : : import_account_keys ( string filename , string password , string src_account_name , string dest_account_name )
{
FC_ASSERT ( ! is_locked ( ) ) ;
FC_ASSERT ( fc : : exists ( filename ) ) ;
bool is_my_account = false ;
const auto accounts = list_my_accounts ( ) ;
for ( const auto & account : accounts )
{
if ( account . name = = dest_account_name )
{
is_my_account = true ;
break ;
}
}
FC_ASSERT ( is_my_account ) ;
const auto imported_keys = fc : : json : : from_file < exported_keys > ( filename ) ;
const auto password_hash = fc : : sha512 : : hash ( password ) ;
FC_ASSERT ( fc : : sha512 : : hash ( password_hash ) = = imported_keys . password_checksum ) ;
bool found_account = false ;
for ( const auto & item : imported_keys . account_keys )
{
if ( item . account_name ! = src_account_name )
continue ;
found_account = true ;
for ( const auto & encrypted_key : item . encrypted_private_keys )
{
const auto plain_text = fc : : aes_decrypt ( password_hash , encrypted_key ) ;
const auto private_key = fc : : raw : : unpack < private_key_type > ( plain_text ) ;
import_key ( dest_account_name , string ( graphene : : utilities : : key_to_wif ( private_key ) ) ) ;
}
return true ;
}
FC_ASSERT ( found_account ) ;
return false ;
}
2015-06-08 15:50:35 +00:00
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 ,
2015-07-08 22:45:53 +00:00
asset_options common ,
fc : : optional < bitasset_options > bitasset_opts ,
2015-06-08 15:50:35 +00:00
bool broadcast )
{
return my - > create_asset ( issuer , symbol , precision , common , bitasset_opts , broadcast ) ;
}
2015-06-19 23:26:29 +00:00
signed_transaction wallet_api : : update_asset ( string symbol ,
optional < string > new_issuer ,
2015-07-08 22:45:53 +00:00
asset_options new_options ,
2015-06-19 23:26:29 +00:00
bool broadcast /* = false */ )
{
return my - > update_asset ( symbol , new_issuer , new_options , broadcast ) ;
}
2015-06-30 20:42:41 +00:00
2015-06-19 23:26:29 +00:00
signed_transaction wallet_api : : update_bitasset ( string symbol ,
2015-07-08 22:45:53 +00:00
bitasset_options new_options ,
2015-06-19 23:26:29 +00:00
bool broadcast /* = false */ )
{
return my - > update_bitasset ( symbol , new_options , broadcast ) ;
}
signed_transaction wallet_api : : update_asset_feed_producers ( string symbol ,
flat_set < string > 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 ) ;
}
2015-07-01 18:43:17 +00:00
signed_transaction wallet_api : : reserve_asset ( string from ,
2015-06-19 23:26:29 +00:00
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 */ )
{
2015-06-20 22:33:58 +00:00
return my - > global_settle_asset ( symbol , settle_price , broadcast ) ;
2015-06-19 23:26:29 +00:00
}
signed_transaction wallet_api : : settle_asset ( string account_to_settle ,
string amount_to_settle ,
string symbol ,
bool broadcast /* = false */ )
{
2015-06-20 22:33:58 +00:00
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 ) ;
2015-06-19 23:26:29 +00:00
}
2015-07-13 20:06:02 +00:00
signed_transaction wallet_api : : create_committee_member ( string owner_account , string url ,
2015-06-22 23:37:18 +00:00
bool broadcast /* = false */ )
{
2015-07-13 20:06:02 +00:00
return my - > create_committee_member ( owner_account , url , broadcast ) ;
2015-06-22 23:37:18 +00:00
}
2015-06-30 21:28:07 +00:00
map < string , witness_id_type > wallet_api : : list_witnesses ( const string & lowerbound , uint32_t limit )
{
return my - > _remote_db - > lookup_witness_accounts ( lowerbound , limit ) ;
}
2015-07-13 20:06:02 +00:00
map < string , committee_member_id_type > wallet_api : : list_committee_members ( const string & lowerbound , uint32_t limit )
2015-07-01 21:47:16 +00:00
{
2015-07-13 20:06:02 +00:00
return my - > _remote_db - > lookup_committee_member_accounts ( lowerbound , limit ) ;
2015-07-01 21:47:16 +00:00
}
2015-06-30 21:28:07 +00:00
witness_object wallet_api : : get_witness ( string owner_account )
{
return my - > get_witness ( owner_account ) ;
}
2015-07-13 20:06:02 +00:00
committee_member_object wallet_api : : get_committee_member ( string owner_account )
2015-07-03 00:05:31 +00:00
{
2015-07-13 20:06:02 +00:00
return my - > get_committee_member ( owner_account ) ;
2015-07-03 00:05:31 +00:00
}
2015-06-22 23:37:18 +00:00
signed_transaction wallet_api : : create_witness ( string owner_account ,
2015-06-30 21:28:07 +00:00
string url ,
2015-06-22 23:37:18 +00:00
bool broadcast /* = false */ )
{
2015-06-30 21:28:07 +00:00
return my - > create_witness ( owner_account , url , broadcast ) ;
2015-06-22 23:37:18 +00:00
}
2015-07-13 20:06:02 +00:00
signed_transaction wallet_api : : vote_for_committee_member ( string voting_account ,
2015-06-22 22:02:51 +00:00
string witness ,
bool approve ,
bool broadcast /* = false */ )
{
2015-07-13 20:06:02 +00:00
return my - > vote_for_committee_member ( voting_account , witness , approve , broadcast ) ;
2015-06-22 22:02:51 +00:00
}
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 ) ;
2015-06-30 20:42:41 +00:00
}
2015-06-22 22:02:51 +00:00
2015-06-22 23:37:18 +00:00
signed_transaction wallet_api : : set_voting_proxy ( string account_to_modify ,
optional < string > voting_account ,
bool broadcast /* = false */ )
{
return my - > set_voting_proxy ( account_to_modify , voting_account , broadcast ) ;
}
2015-07-13 20:06:02 +00:00
signed_transaction wallet_api : : set_desired_witness_and_committee_member_count ( string account_to_modify ,
2015-07-06 19:27:42 +00:00
uint16_t desired_number_of_witnesses ,
2015-07-13 20:06:02 +00:00
uint16_t desired_number_of_committee_members ,
2015-07-06 19:27:42 +00:00
bool broadcast /* = false */ )
{
2015-07-13 20:06:02 +00:00
return my - > set_desired_witness_and_committee_member_count ( account_to_modify , desired_number_of_witnesses ,
desired_number_of_committee_members , broadcast ) ;
2015-07-06 19:27:42 +00:00
}
2015-06-08 15:50:35 +00:00
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 ) ) }
2015-07-06 16:44:07 +00:00
operation wallet_api : : get_prototype_operation ( string operation_name )
{
2015-07-15 21:42:23 +00:00
return my - > get_prototype_operation ( operation_name ) ;
2015-07-06 16:44:07 +00:00
}
2015-06-08 15:50:35 +00:00
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
{
2015-06-26 21:47:03 +00:00
std : : vector < std : : string > method_names = my - > method_documentation . get_method_names ( ) ;
2015-06-08 15:50:35 +00:00
std : : stringstream ss ;
2015-06-26 21:47:03 +00:00
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 " ;
}
}
2015-06-08 15:50:35 +00:00
return ss . str ( ) ;
}
2015-06-26 21:47:03 +00:00
string wallet_api : : gethelp ( const string & method ) const
2015-06-08 15:50:35 +00:00
{
fc : : api < wallet_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 " ;
2015-07-08 22:45:53 +00:00
ss < < fc : : json : : to_pretty_string ( graphene : : chain : : asset_options ( ) ) ;
2015-06-08 15:50:35 +00:00
ss < < " \n Example value of BITASSET_OPTIONS: \n " ;
2015-07-08 22:45:53 +00:00
ss < < fc : : json : : to_pretty_string ( graphene : : chain : : bitasset_options ( ) ) ;
2015-06-08 15:50:35 +00:00
ss < < " \n BITASSET_OPTIONS may be null \n " ;
}
else
{
2015-06-26 21:47:03 +00:00
std : : string doxygenHelpString = my - > method_documentation . get_detailed_description ( method ) ;
if ( ! doxygenHelpString . empty ( ) )
ss < < doxygenHelpString ;
else
ss < < " No help defined for method " < < method < < " \n " ;
2015-06-08 15:50:35 +00:00
}
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 < string , std : : function < string ( fc : : variant , const fc : : variants & ) > >
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 )
2015-07-23 15:29:55 +00:00
key . second = key_to_wif ( fc : : ecc : : private_key ( ) ) ;
2015-06-08 15:50:35 +00:00
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 < char > decrypted = fc : : aes_decrypt ( pw , my - > _wallet . cipher_keys ) ;
auto pk = fc : : raw : : unpack < plain_keys > ( 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 ( ) ;
}
2015-06-28 20:16:24 +00:00
signed_transaction wallet_api : : import_balance ( string name_or_id , const vector < string > & wif_keys , bool broadcast )
{ try {
FC_ASSERT ( ! is_locked ( ) ) ;
account_object claimer = get_account ( name_or_id ) ;
vector < address > addrs ;
map < address , private_key_type > 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 < asset_id_type > bal_types ;
for ( auto b : balances ) bal_types . insert ( b . balance . asset_id ) ;
set < address > required_addrs ;
signed_transaction trx ;
for ( auto a : bal_types )
{
balance_claim_operation op ;
2015-06-30 20:42:41 +00:00
op . deposit_to_account = claimer . id ;
for ( const auto & b : balances )
2015-06-28 20:16:24 +00:00
{
if ( b . balance . asset_id = = a )
{
2015-06-30 20:42:41 +00:00
op . total_claimed = b . vesting_policy . valid ( ) ? asset ( 0 , b . balance . asset_id ) : b . balance ;
op . balance_to_claim = b . id ;
2015-07-02 15:18:52 +00:00
op . balance_owner_key = keys [ b . owner ] . get_public_key ( ) ;
2015-06-30 20:42:41 +00:00
trx . operations . push_back ( op ) ;
required_addrs . insert ( b . owner ) ;
2015-06-28 20:16:24 +00:00
}
}
}
2015-07-08 22:45:53 +00:00
my - > set_operation_fees ( trx , my - > _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
2015-06-28 20:16:24 +00:00
trx . validate ( ) ;
auto tx = sign_transaction ( trx , false ) ;
for ( auto a : required_addrs )
2015-08-06 16:41:45 +00:00
tx . sign ( keys [ a ] , my - > _chain_id ) ;
2015-07-05 20:02:59 +00:00
// 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 : : return_found_end > ( boost : : sort ( tx . signatures ) ) ) ;
2015-06-30 20:42:41 +00:00
if ( broadcast )
2015-07-06 18:36:06 +00:00
my - > _remote_net_broadcast - > broadcast_transaction ( tx ) ;
2015-06-28 20:16:24 +00:00
return tx ;
} FC_CAPTURE_AND_RETHROW ( ( name_or_id ) ) }
2015-07-02 05:52:45 +00:00
map < public_key_type , string > wallet_api : : dump_private_keys ( )
2015-06-08 15:50:35 +00:00
{
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 ) ;
}
2015-06-16 15:22:28 +00:00
signed_transaction wallet_api : : borrow_asset ( string seller_name , string amount_to_sell ,
2015-06-08 15:50:35 +00:00
string asset_symbol , string amount_of_collateral , bool broadcast )
{
FC_ASSERT ( ! is_locked ( ) ) ;
2015-06-16 15:22:28 +00:00
return my - > borrow_asset ( seller_name , amount_to_sell , asset_symbol , amount_of_collateral , broadcast ) ;
2015-06-08 15:50:35 +00:00
}
2015-07-23 22:46:06 +00:00
string wallet_api : : get_key_label ( public_key_type key ) const
{
auto key_itr = my - > _wallet . labeled_keys . get < by_key > ( ) . find ( key ) ;
if ( key_itr ! = my - > _wallet . labeled_keys . get < by_key > ( ) . end ( ) )
return key_itr - > label ;
return string ( ) ;
}
2015-08-20 22:41:54 +00:00
string wallet_api : : get_private_key ( public_key_type pubkey ) const
{
return key_to_wif ( my - > get_private_key ( pubkey ) ) ;
}
2015-07-23 22:46:06 +00:00
public_key_type wallet_api : : get_public_key ( string label ) const
{
2015-07-24 21:02:06 +00:00
try { return fc : : variant ( label ) . as < public_key_type > ( ) ; } catch ( . . . ) { }
2015-07-23 22:46:06 +00:00
auto key_itr = my - > _wallet . labeled_keys . get < by_label > ( ) . find ( label ) ;
if ( key_itr ! = my - > _wallet . labeled_keys . get < by_label > ( ) . end ( ) )
return key_itr - > key ;
return public_key_type ( ) ;
}
bool wallet_api : : set_key_label ( public_key_type key , string label )
{
auto result = my - > _wallet . labeled_keys . insert ( key_label { label , key } ) ;
if ( result . second ) return true ;
auto key_itr = my - > _wallet . labeled_keys . get < by_key > ( ) . find ( key ) ;
auto label_itr = my - > _wallet . labeled_keys . get < by_label > ( ) . find ( label ) ;
if ( label_itr = = my - > _wallet . labeled_keys . get < by_label > ( ) . end ( ) )
{
if ( key_itr ! = my - > _wallet . labeled_keys . get < by_key > ( ) . end ( ) )
return my - > _wallet . labeled_keys . get < by_key > ( ) . modify ( key_itr , [ & ] ( key_label & obj ) { obj . label = label ; } ) ;
}
return false ;
}
map < string , public_key_type > wallet_api : : get_blind_accounts ( ) const
{
map < string , public_key_type > result ;
for ( const auto & item : my - > _wallet . labeled_keys )
result [ item . label ] = item . key ;
return result ;
}
map < string , public_key_type > wallet_api : : get_my_blind_accounts ( ) const
{
FC_ASSERT ( ! is_locked ( ) ) ;
map < string , public_key_type > result ;
for ( const auto & item : my - > _wallet . labeled_keys )
{
if ( my - > _keys . find ( item . key ) ! = my - > _keys . end ( ) )
result [ item . label ] = item . key ;
}
return result ;
}
public_key_type wallet_api : : create_blind_account ( string label , string brain_key )
{
FC_ASSERT ( ! is_locked ( ) ) ;
auto label_itr = my - > _wallet . labeled_keys . get < by_label > ( ) . find ( label ) ;
if ( label_itr ! = my - > _wallet . labeled_keys . get < by_label > ( ) . end ( ) )
FC_ASSERT ( ! " Key with label already exists " ) ;
brain_key = fc : : trim_and_normalize_spaces ( brain_key ) ;
auto secret = fc : : sha256 : : hash ( brain_key . c_str ( ) , brain_key . size ( ) ) ;
auto priv_key = fc : : ecc : : private_key : : regenerate ( secret ) ;
public_key_type pub_key = priv_key . get_public_key ( ) ;
FC_ASSERT ( set_key_label ( pub_key , label ) ) ;
my - > _keys [ pub_key ] = graphene : : utilities : : key_to_wif ( priv_key ) ;
save_wallet_file ( ) ;
return pub_key ;
}
vector < asset > wallet_api : : get_blind_balances ( string key_or_label )
{
vector < asset > result ;
2015-07-24 21:02:06 +00:00
map < asset_id_type , share_type > balances ;
vector < commitment_type > used ;
2015-07-23 22:46:06 +00:00
auto pub_key = get_public_key ( key_or_label ) ;
2015-07-24 21:02:06 +00:00
auto & to_asset_used_idx = my - > _wallet . blind_receipts . get < by_to_asset_used > ( ) ;
auto start = to_asset_used_idx . lower_bound ( std : : make_tuple ( pub_key , 0 , false ) ) ;
auto end = to_asset_used_idx . lower_bound ( std : : make_tuple ( pub_key , uint32_t ( 0xffffffff ) , true ) ) ;
while ( start ! = end )
2015-07-23 22:46:06 +00:00
{
2015-07-24 21:02:06 +00:00
if ( ! start - > used )
2015-07-23 22:46:06 +00:00
{
2015-07-24 21:02:06 +00:00
auto answer = my - > _remote_db - > get_blinded_balances ( { start - > commitment ( ) } ) ;
if ( answer . size ( ) )
balances [ start - > amount . asset_id ] + = start - > amount . amount ;
else
used . push_back ( start - > commitment ( ) ) ;
2015-07-23 22:46:06 +00:00
}
2015-07-24 21:02:06 +00:00
+ + start ;
2015-07-23 22:46:06 +00:00
}
2015-07-24 21:02:06 +00:00
for ( const auto & u : used )
2015-07-23 22:46:06 +00:00
{
2015-07-24 21:02:06 +00:00
auto itr = my - > _wallet . blind_receipts . get < by_commitment > ( ) . find ( u ) ;
my - > _wallet . blind_receipts . modify ( itr , [ ] ( blind_receipt & r ) { r . used = true ; } ) ;
2015-07-23 22:46:06 +00:00
}
2015-07-24 21:02:06 +00:00
for ( auto item : balances )
result . push_back ( asset ( item . second , item . first ) ) ;
2015-07-23 22:46:06 +00:00
return result ;
}
2015-07-24 21:02:06 +00:00
blind_confirmation wallet_api : : transfer_from_blind ( string from_blind_account_key_or_label ,
string to_account_id_or_name ,
string amount_in ,
string symbol ,
bool broadcast )
{ try {
transfer_from_blind_operation from_blind ;
auto fees = my - > _remote_db - > get_global_properties ( ) . parameters . current_fees ;
fc : : optional < asset_object > asset_obj = get_asset ( symbol ) ;
FC_ASSERT ( asset_obj . valid ( ) , " Could not find asset matching ${asset} " , ( " asset " , symbol ) ) ;
auto amount = asset_obj - > amount_from_string ( amount_in ) ;
from_blind . fee = fees - > calculate_fee ( from_blind , asset_obj - > options . core_exchange_rate ) ;
auto blind_in = asset_obj - > amount_to_string ( from_blind . fee + amount ) ;
auto conf = blind_transfer_help ( from_blind_account_key_or_label ,
from_blind_account_key_or_label ,
blind_in , symbol , false , true ) ;
FC_ASSERT ( conf . outputs . size ( ) > 0 ) ;
auto to_account = my - > get_account ( to_account_id_or_name ) ;
from_blind . to = to_account . id ;
from_blind . amount = amount ;
from_blind . blinding_factor = conf . outputs . back ( ) . decrypted_memo . blinding_factor ;
from_blind . inputs . push_back ( { conf . outputs . back ( ) . decrypted_memo . commitment , authority ( ) } ) ;
from_blind . fee = fees - > calculate_fee ( from_blind , asset_obj - > options . core_exchange_rate ) ;
idump ( ( from_blind ) ) ;
conf . trx . operations . push_back ( from_blind ) ;
ilog ( " about to validate " ) ;
conf . trx . validate ( ) ;
ilog ( " about to broadcast " ) ;
conf . trx = sign_transaction ( conf . trx , broadcast ) ;
return conf ;
} FC_CAPTURE_AND_RETHROW ( ( from_blind_account_key_or_label ) ( to_account_id_or_name ) ( amount_in ) ( symbol ) ) }
2015-07-23 22:46:06 +00:00
blind_confirmation wallet_api : : blind_transfer ( string from_key_or_label ,
string to_key_or_label ,
string amount_in ,
string symbol ,
bool broadcast )
2015-07-24 21:02:06 +00:00
{
return blind_transfer_help ( from_key_or_label , to_key_or_label , amount_in , symbol , broadcast , false ) ;
}
blind_confirmation wallet_api : : blind_transfer_help ( string from_key_or_label ,
string to_key_or_label ,
string amount_in ,
string symbol ,
bool broadcast ,
bool to_temp )
2015-07-24 15:37:37 +00:00
{
blind_confirmation confirm ;
try {
2015-07-23 22:46:06 +00:00
FC_ASSERT ( ! is_locked ( ) ) ;
public_key_type from_key = get_public_key ( from_key_or_label ) ;
public_key_type to_key = get_public_key ( to_key_or_label ) ;
fc : : optional < asset_object > asset_obj = get_asset ( symbol ) ;
FC_ASSERT ( asset_obj . valid ( ) , " Could not find asset matching ${asset} " , ( " asset " , symbol ) ) ;
blind_transfer_operation blind_tr ;
blind_tr . outputs . resize ( 2 ) ;
auto fees = my - > _remote_db - > get_global_properties ( ) . parameters . current_fees ;
auto amount = asset_obj - > amount_from_string ( amount_in ) ;
asset total_amount = asset_obj - > amount ( 0 ) ;
vector < fc : : sha256 > blinding_factors ;
//auto from_priv_key = my->get_private_key( from_key );
blind_tr . fee = fees - > calculate_fee ( blind_tr , asset_obj - > options . core_exchange_rate ) ;
2015-07-24 21:02:06 +00:00
vector < commitment_type > used ;
auto & to_asset_used_idx = my - > _wallet . blind_receipts . get < by_to_asset_used > ( ) ;
auto start = to_asset_used_idx . lower_bound ( std : : make_tuple ( from_key , amount . asset_id , false ) ) ;
auto end = to_asset_used_idx . lower_bound ( std : : make_tuple ( from_key , amount . asset_id , true ) ) ;
while ( start ! = end )
2015-07-23 22:46:06 +00:00
{
2015-07-24 21:02:06 +00:00
auto result = my - > _remote_db - > get_blinded_balances ( { start - > commitment ( ) } ) ;
if ( result . size ( ) = = 0 )
2015-07-23 22:46:06 +00:00
{
2015-07-24 21:02:06 +00:00
used . push_back ( start - > commitment ( ) ) ;
}
else
{
blind_tr . inputs . push_back ( { start - > commitment ( ) , start - > control_authority } ) ;
blinding_factors . push_back ( start - > data . blinding_factor ) ;
total_amount + = start - > amount ;
2015-07-23 22:46:06 +00:00
2015-07-24 21:02:06 +00:00
if ( total_amount > = amount + blind_tr . fee )
break ;
2015-07-23 22:46:06 +00:00
}
2015-07-24 21:02:06 +00:00
+ + start ;
}
for ( const auto & u : used )
{
auto itr = my - > _wallet . blind_receipts . get < by_commitment > ( ) . find ( u ) ;
my - > _wallet . blind_receipts . modify ( itr , [ ] ( blind_receipt & r ) { r . used = true ; } ) ;
2015-07-23 22:46:06 +00:00
}
FC_ASSERT ( total_amount > = amount + blind_tr . fee , " Insufficent Balance " , ( " available " , total_amount ) ( " amount " , amount ) ( " fee " , blind_tr . fee ) ) ;
auto one_time_key = fc : : ecc : : private_key : : generate ( ) ;
auto secret = one_time_key . get_shared_secret ( to_key ) ;
auto child = fc : : sha256 : : hash ( secret ) ;
auto nonce = fc : : sha256 : : hash ( one_time_key . get_secret ( ) ) ;
auto blind_factor = fc : : sha256 : : hash ( child ) ;
auto from_secret = one_time_key . get_shared_secret ( from_key ) ;
auto from_child = fc : : sha256 : : hash ( from_secret ) ;
auto from_nonce = fc : : sha256 : : hash ( nonce ) ;
auto change = total_amount - amount - blind_tr . fee ;
fc : : sha256 change_blind_factor ;
fc : : sha256 to_blind_factor ;
if ( change . amount > 0 )
{
2015-07-24 15:37:37 +00:00
idump ( ( " to_blind_factor " ) ( blind_factor ) ) ;
2015-07-23 22:46:06 +00:00
blinding_factors . push_back ( blind_factor ) ;
change_blind_factor = fc : : ecc : : blind_sum ( blinding_factors , blinding_factors . size ( ) - 1 ) ;
2015-07-24 15:37:37 +00:00
wdump ( ( " change_blind_factor " ) ( change_blind_factor ) ) ;
2015-07-23 22:46:06 +00:00
}
else // change == 0
{
blind_tr . outputs . resize ( 1 ) ;
blind_factor = fc : : ecc : : blind_sum ( blinding_factors , blinding_factors . size ( ) ) ;
2015-07-24 21:02:06 +00:00
idump ( ( " to_sum_blind_factor " ) ( blind_factor ) ) ;
2015-07-23 22:46:06 +00:00
blinding_factors . push_back ( blind_factor ) ;
2015-07-24 15:37:37 +00:00
idump ( ( " nochange to_blind_factor " ) ( blind_factor ) ) ;
2015-07-23 22:46:06 +00:00
}
fc : : ecc : : public_key from_pub_key = from_key ;
fc : : ecc : : public_key to_pub_key = to_key ;
2015-07-24 15:37:37 +00:00
blind_output to_out ;
2015-07-24 21:02:06 +00:00
to_out . owner = to_temp ? authority ( ) : authority ( 1 , public_key_type ( to_pub_key . child ( child ) ) , 1 ) ;
2015-07-24 15:37:37 +00:00
to_out . commitment = fc : : ecc : : blind ( blind_factor , amount . amount . value ) ;
2015-07-24 21:02:06 +00:00
idump ( ( " to_out.blind " ) ( blind_factor ) ( to_out . commitment ) ) ;
2015-07-23 22:46:06 +00:00
if ( blind_tr . outputs . size ( ) > 1 )
{
2015-07-24 15:37:37 +00:00
to_out . range_proof = fc : : ecc : : range_proof_sign ( 0 , to_out . commitment , blind_factor , nonce , 0 , 0 , amount . amount . value ) ;
2015-07-23 22:46:06 +00:00
blind_output change_out ;
2015-07-24 15:37:37 +00:00
change_out . owner = authority ( 1 , public_key_type ( from_pub_key . child ( from_child ) ) , 1 ) ;
2015-07-23 22:46:06 +00:00
change_out . commitment = fc : : ecc : : blind ( change_blind_factor , change . amount . value ) ;
2015-07-24 15:37:37 +00:00
change_out . range_proof = fc : : ecc : : range_proof_sign ( 0 , change_out . commitment , change_blind_factor , from_nonce , 0 , 0 , change . amount . value ) ;
2015-07-23 22:46:06 +00:00
blind_tr . outputs [ 1 ] = change_out ;
blind_confirmation : : output conf_output ;
conf_output . label = from_key_or_label ;
conf_output . pub_key = from_key ;
conf_output . decrypted_memo . from = from_key ;
conf_output . decrypted_memo . amount = change ;
conf_output . decrypted_memo . blinding_factor = change_blind_factor ;
conf_output . decrypted_memo . commitment = change_out . commitment ;
conf_output . decrypted_memo . check = from_secret . _hash [ 0 ] ;
conf_output . confirmation . one_time_key = one_time_key . get_public_key ( ) ;
conf_output . confirmation . to = from_key ;
2015-07-24 15:37:37 +00:00
conf_output . confirmation . encrypted_memo = fc : : aes_encrypt ( from_secret , fc : : raw : : pack ( conf_output . decrypted_memo ) ) ;
2015-07-24 21:02:06 +00:00
conf_output . auth = change_out . owner ;
2015-07-23 22:46:06 +00:00
conf_output . confirmation_receipt = conf_output . confirmation ;
confirm . outputs . push_back ( conf_output ) ;
}
2015-07-24 15:37:37 +00:00
blind_tr . outputs [ 0 ] = to_out ;
2015-07-23 22:46:06 +00:00
blind_confirmation : : output conf_output ;
conf_output . label = to_key_or_label ;
conf_output . pub_key = to_key ;
conf_output . decrypted_memo . from = from_key ;
conf_output . decrypted_memo . amount = amount ;
conf_output . decrypted_memo . blinding_factor = blind_factor ;
2015-07-24 15:37:37 +00:00
conf_output . decrypted_memo . commitment = to_out . commitment ;
2015-07-23 22:46:06 +00:00
conf_output . decrypted_memo . check = secret . _hash [ 0 ] ;
conf_output . confirmation . one_time_key = one_time_key . get_public_key ( ) ;
conf_output . confirmation . to = to_key ;
conf_output . confirmation . encrypted_memo = fc : : aes_encrypt ( secret , fc : : raw : : pack ( conf_output . decrypted_memo ) ) ;
2015-07-24 21:02:06 +00:00
conf_output . auth = to_out . owner ;
2015-07-23 22:46:06 +00:00
conf_output . confirmation_receipt = conf_output . confirmation ;
confirm . outputs . push_back ( conf_output ) ;
/** commitments must be in sorted order */
std : : sort ( blind_tr . outputs . begin ( ) , blind_tr . outputs . end ( ) ,
[ & ] ( const blind_output & a , const blind_output & b ) { return a . commitment < b . commitment ; } ) ;
2015-07-24 21:02:06 +00:00
std : : sort ( blind_tr . inputs . begin ( ) , blind_tr . inputs . end ( ) ,
[ & ] ( const blind_input & a , const blind_input & b ) { return a . commitment < b . commitment ; } ) ;
2015-07-23 22:46:06 +00:00
2015-07-24 15:37:37 +00:00
confirm . trx . operations . emplace_back ( std : : move ( blind_tr ) ) ;
ilog ( " validate before " ) ;
2015-07-23 22:46:06 +00:00
confirm . trx . validate ( ) ;
confirm . trx = sign_transaction ( confirm . trx , broadcast ) ;
2015-07-24 21:02:06 +00:00
if ( broadcast )
{
for ( const auto & out : confirm . outputs )
2015-07-27 16:09:34 +00:00
{
2015-07-24 21:02:06 +00:00
try { receive_blind_transfer ( out . confirmation_receipt , from_key_or_label , " " ) ; } catch ( . . . ) { }
2015-07-27 16:09:34 +00:00
}
2015-07-24 21:02:06 +00:00
}
2015-07-23 22:46:06 +00:00
2015-07-24 15:37:37 +00:00
return confirm ;
} FC_CAPTURE_AND_RETHROW ( ( from_key_or_label ) ( to_key_or_label ) ( amount_in ) ( symbol ) ( broadcast ) ( confirm ) ) }
2015-07-23 22:46:06 +00:00
/**
* Transfers a public balance from @ from to one or more blinded balances using a
* stealth transfer .
*/
blind_confirmation wallet_api : : transfer_to_blind ( string from_account_id_or_name ,
string asset_symbol ,
/** map from key or label to amount */
map < string , string > to_amounts ,
bool broadcast )
{ try {
FC_ASSERT ( ! is_locked ( ) ) ;
blind_confirmation confirm ;
account_object from_account = my - > get_account ( from_account_id_or_name ) ;
fc : : optional < asset_object > asset_obj = get_asset ( asset_symbol ) ;
FC_ASSERT ( asset_obj , " Could not find asset matching ${asset} " , ( " asset " , asset_symbol ) ) ;
transfer_to_blind_operation bop ;
bop . from = from_account . id ;
vector < fc : : sha256 > blinding_factors ;
asset total_amount = asset_obj - > amount ( 0 ) ;
for ( auto item : to_amounts )
{
auto one_time_key = fc : : ecc : : private_key : : generate ( ) ;
auto to_key = get_public_key ( item . first ) ;
auto secret = one_time_key . get_shared_secret ( to_key ) ;
auto child = fc : : sha256 : : hash ( secret ) ;
auto nonce = fc : : sha256 : : hash ( one_time_key . get_secret ( ) ) ;
auto blind_factor = fc : : sha256 : : hash ( child ) ;
blinding_factors . push_back ( blind_factor ) ;
auto amount = asset_obj - > amount_from_string ( item . second ) ;
total_amount + = amount ;
fc : : ecc : : public_key to_pub_key = to_key ;
blind_output out ;
out . owner = authority ( 1 , public_key_type ( to_pub_key . child ( child ) ) , 1 ) ;
out . commitment = fc : : ecc : : blind ( blind_factor , amount . amount . value ) ;
if ( to_amounts . size ( ) > 1 )
out . range_proof = fc : : ecc : : range_proof_sign ( 0 , out . commitment , blind_factor , nonce , 0 , 0 , amount . amount . value ) ;
blind_confirmation : : output conf_output ;
conf_output . label = item . first ;
conf_output . pub_key = to_key ;
conf_output . decrypted_memo . amount = amount ;
conf_output . decrypted_memo . blinding_factor = blind_factor ;
conf_output . decrypted_memo . commitment = out . commitment ;
conf_output . decrypted_memo . check = secret . _hash [ 0 ] ;
conf_output . confirmation . one_time_key = one_time_key . get_public_key ( ) ;
conf_output . confirmation . to = to_key ;
conf_output . confirmation . encrypted_memo = fc : : aes_encrypt ( secret , fc : : raw : : pack ( conf_output . decrypted_memo ) ) ;
conf_output . confirmation_receipt = conf_output . confirmation ;
confirm . outputs . push_back ( conf_output ) ;
bop . outputs . push_back ( out ) ;
}
bop . amount = total_amount ;
bop . blinding_factor = fc : : ecc : : blind_sum ( blinding_factors , blinding_factors . size ( ) ) ;
/** commitments must be in sorted order */
std : : sort ( bop . outputs . begin ( ) , bop . outputs . end ( ) ,
[ & ] ( const blind_output & a , const blind_output & b ) { return a . commitment < b . commitment ; } ) ;
confirm . trx . operations . push_back ( bop ) ;
my - > set_operation_fees ( confirm . trx , my - > _remote_db - > get_global_properties ( ) . parameters . current_fees ) ;
confirm . trx . validate ( ) ;
confirm . trx = sign_transaction ( confirm . trx , broadcast ) ;
2015-07-24 21:02:06 +00:00
if ( broadcast )
{
for ( const auto & out : confirm . outputs )
2015-07-27 16:09:34 +00:00
{
2015-07-24 21:02:06 +00:00
try { receive_blind_transfer ( out . confirmation_receipt , " @ " + from_account . name , " from @ " + from_account . name ) ; } catch ( . . . ) { }
2015-07-27 16:09:34 +00:00
}
2015-07-24 21:02:06 +00:00
}
2015-07-23 22:46:06 +00:00
return confirm ;
} FC_CAPTURE_AND_RETHROW ( ( from_account_id_or_name ) ( asset_symbol ) ( to_amounts ) ) }
2015-07-24 21:02:06 +00:00
blind_receipt wallet_api : : receive_blind_transfer ( string confirmation_receipt , string opt_from , string opt_memo )
2015-07-23 22:46:06 +00:00
{
FC_ASSERT ( ! is_locked ( ) ) ;
stealth_confirmation conf ( confirmation_receipt ) ;
FC_ASSERT ( conf . to ) ;
2015-07-24 21:02:06 +00:00
blind_receipt result ;
result . conf = conf ;
2015-07-23 22:46:06 +00:00
auto to_priv_key_itr = my - > _keys . find ( * conf . to ) ;
FC_ASSERT ( to_priv_key_itr ! = my - > _keys . end ( ) , " No private key for receiver " , ( " conf " , conf ) ) ;
2015-07-24 21:02:06 +00:00
2015-07-23 22:46:06 +00:00
auto to_priv_key = wif_to_key ( to_priv_key_itr - > second ) ;
FC_ASSERT ( to_priv_key ) ;
auto secret = to_priv_key - > get_shared_secret ( conf . one_time_key ) ;
auto child = fc : : sha256 : : hash ( secret ) ;
2015-07-24 15:37:37 +00:00
auto child_priv_key = to_priv_key - > child ( child ) ;
2015-07-23 22:46:06 +00:00
//auto blind_factor = fc::sha256::hash( child );
auto plain_memo = fc : : aes_decrypt ( secret , conf . encrypted_memo ) ;
auto memo = fc : : raw : : unpack < stealth_confirmation : : memo_data > ( plain_memo ) ;
2015-07-24 21:02:06 +00:00
result . to_key = * conf . to ;
result . to_label = get_key_label ( result . to_key ) ;
if ( memo . from )
{
result . from_key = * memo . from ;
result . from_label = get_key_label ( result . from_key ) ;
if ( result . from_label = = string ( ) )
{
result . from_label = opt_from ;
set_key_label ( result . from_key , result . from_label ) ;
}
}
else
{
result . from_label = opt_from ;
}
result . amount = memo . amount ;
result . memo = opt_memo ;
// confirm the amount matches the commitment (verify the blinding factor)
auto commtiment_test = fc : : ecc : : blind ( memo . blinding_factor , memo . amount . amount . value ) ;
FC_ASSERT ( fc : : ecc : : verify_sum ( { commtiment_test } , { memo . commitment } , 0 ) ) ;
2015-07-23 22:46:06 +00:00
auto bbal = my - > _remote_db - > get_blinded_balances ( { memo . commitment } ) ;
FC_ASSERT ( bbal . size ( ) , " commitment not found in blockchain " , ( " memo " , memo ) ) ;
blind_balance bal ;
bal . amount = memo . amount ;
bal . to = * conf . to ;
if ( memo . from ) bal . from = * memo . from ;
bal . one_time_key = conf . one_time_key ;
bal . blinding_factor = memo . blinding_factor ;
bal . commitment = memo . commitment ;
bal . used = false ;
2015-07-24 21:02:06 +00:00
result . control_authority = bbal . front ( ) . owner ;
result . data = memo ;
2015-07-24 15:37:37 +00:00
auto child_key_itr = bbal . front ( ) . owner . key_auths . find ( child_priv_key . get_public_key ( ) ) ;
if ( child_key_itr ! = bbal . front ( ) . owner . key_auths . end ( ) )
my - > _keys [ child_key_itr - > first ] = key_to_wif ( child_priv_key ) ;
2015-07-23 22:46:06 +00:00
2015-07-24 21:02:06 +00:00
// my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );
2015-07-23 22:46:06 +00:00
2015-07-24 21:02:06 +00:00
result . date = fc : : time_point : : now ( ) ;
my - > _wallet . blind_receipts . insert ( result ) ;
2015-07-24 15:37:37 +00:00
my - > _keys [ child_priv_key . get_public_key ( ) ] = key_to_wif ( child_priv_key ) ;
save_wallet_file ( ) ;
2015-07-23 22:46:06 +00:00
2015-07-24 21:02:06 +00:00
return result ;
}
vector < blind_receipt > wallet_api : : blind_history ( string key_or_account )
{
vector < blind_receipt > result ;
auto pub_key = get_public_key ( key_or_account ) ;
if ( pub_key = = public_key_type ( ) )
return vector < blind_receipt > ( ) ;
for ( auto & r : my - > _wallet . blind_receipts )
{
if ( r . from_key = = pub_key | | r . to_key = = pub_key )
result . push_back ( r ) ;
}
std : : sort ( result . begin ( ) , result . end ( ) , [ & ] ( const blind_receipt & a , const blind_receipt & b ) { return a . date > b . date ; } ) ;
return result ;
2015-07-23 22:46:06 +00:00
}
} } // graphene::wallet
2015-06-08 15:50:35 +00:00
2015-06-18 13:19:14 +00:00
void fc : : to_variant ( const account_multi_index_type & accts , fc : : variant & vo )
2015-06-08 15:50:35 +00:00
{
vo = vector < account_object > ( accts . begin ( ) , accts . end ( ) ) ;
}
2015-06-18 13:19:14 +00:00
void fc : : from_variant ( const fc : : variant & var , account_multi_index_type & vo )
2015-06-08 15:50:35 +00:00
{
const vector < account_object > & v = var . as < vector < account_object > > ( ) ;
2015-06-18 13:19:14 +00:00
vo = account_multi_index_type ( v . begin ( ) , v . end ( ) ) ;
2015-06-08 15:50:35 +00:00
}