2015-05-27 12:08:46 +00:00
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
2014-08-13 10:38:35 +00:00
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
# include "simplewallet.h"
# include <future>
2014-03-03 22:07:58 +00:00
# include <thread>
2015-04-06 16:13:07 +00:00
# include <set>
# include <sstream>
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
# include <boost/lexical_cast.hpp>
# include <boost/program_options.hpp>
# include <boost/algorithm/string.hpp>
2015-05-27 12:08:46 +00:00
# include <boost/filesystem.hpp>
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
# include "Common/command_line.h"
# include "Common/SignalHandler.h"
# include "Common/PathTools.h"
# include "Common/util.h"
2014-03-03 22:07:58 +00:00
# include "cryptonote_core/cryptonote_format_utils.h"
2014-08-13 10:51:37 +00:00
# include "cryptonote_protocol/cryptonote_protocol_handler.h"
2015-05-27 12:08:46 +00:00
# include "node_rpc_proxy/NodeRpcProxy.h"
2014-03-03 22:07:58 +00:00
# include "rpc/core_rpc_server_commands_defs.h"
2015-05-27 12:08:46 +00:00
# include "rpc/HttpClient.h"
2014-04-02 16:00:17 +00:00
# include "wallet/wallet_rpc_server.h"
2015-04-06 16:13:07 +00:00
# include "wallet/Wallet.h"
# include "wallet/LegacyKeysImporter.h"
2015-05-27 12:08:46 +00:00
# include "wallet/WalletHelper.h"
# include "version.h"
# include <Logging/LoggerManager.h>
2014-03-03 22:07:58 +00:00
# if defined(WIN32)
# include <crtdbg.h>
# endif
2015-04-06 16:13:07 +00:00
using namespace CryptoNote ;
2015-05-27 12:08:46 +00:00
using namespace Logging ;
using Common : : JsonValue ;
2014-03-03 22:07:58 +00:00
namespace po = boost : : program_options ;
# define EXTENDED_LOGS_FILE "wallet_details.log"
2015-04-06 16:13:07 +00:00
namespace {
const command_line : : arg_descriptor < std : : string > arg_wallet_file = { " wallet-file " , " Use wallet <arg> " , " " } ;
const command_line : : arg_descriptor < std : : string > arg_generate_new_wallet = { " generate-new-wallet " , " Generate new wallet and save it to <arg> " , " " } ;
const command_line : : arg_descriptor < std : : string > arg_daemon_address = { " daemon-address " , " Use daemon instance at <host>:<port> " , " " } ;
const command_line : : arg_descriptor < std : : string > arg_daemon_host = { " daemon-host " , " Use daemon instance at host <arg> instead of localhost " , " " } ;
const command_line : : arg_descriptor < std : : string > arg_password = { " password " , " Wallet password " , " " , true } ;
2015-05-27 12:08:46 +00:00
const command_line : : arg_descriptor < uint16_t > arg_daemon_port = { " daemon-port " , " Use daemon instance at port <arg> instead of 8081 " , 0 } ;
const command_line : : arg_descriptor < uint32_t > arg_log_level = { " set_log " , " " , INFO , true } ;
2015-04-06 16:13:07 +00:00
const command_line : : arg_descriptor < bool > arg_testnet = { " testnet " , " Used to deploy test nets. The daemon must be launched with --testnet flag " , false } ;
const command_line : : arg_descriptor < std : : vector < std : : string > > arg_command = { " command " , " " } ;
2015-05-27 12:08:46 +00:00
bool parseUrlAddress ( const std : : string & url , std : : string & address , uint16_t & port ) {
auto pos = url . find ( " :// " ) ;
size_t addrStart = 0 ;
if ( pos = = std : : string : : npos ) {
pos = 0 ;
} else {
addrStart = pos + 3 ;
}
auto addrEnd = url . find ( ' : ' , addrStart ) ;
if ( addrEnd ! = std : : string : : npos ) {
auto portEnd = url . find ( ' / ' , addrEnd ) ;
port = Common : : fromString < uint16_t > ( url . substr (
addrEnd + 1 , portEnd = = std : : string : : npos ? std : : string : : npos : portEnd - addrEnd - 1 ) ) ;
} else {
addrEnd = url . find ( ' / ' ) ;
port = 80 ;
}
address = url . substr ( addrStart , addrEnd - addrStart ) ;
return true ;
}
2015-04-06 16:13:07 +00:00
inline std : : string interpret_rpc_response ( bool ok , const std : : string & status ) {
std : : string err ;
if ( ok ) {
if ( status = = CORE_RPC_STATUS_BUSY ) {
err = " daemon is busy. Please try later " ;
} else if ( status ! = CORE_RPC_STATUS_OK ) {
err = status ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
} else {
err = " possible lost connection to daemon " ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
return err ;
}
2014-04-02 16:00:17 +00:00
2015-04-06 16:13:07 +00:00
template < typename IterT , typename ValueT = typename IterT : : value_type >
class ArgumentReader {
public :
ArgumentReader ( IterT begin , IterT end ) :
m_begin ( begin ) , m_end ( end ) , m_cur ( begin ) {
2014-03-20 11:46:11 +00:00
}
2014-03-03 22:07:58 +00:00
2015-04-06 16:13:07 +00:00
bool eof ( ) const {
return m_cur = = m_end ;
2014-03-20 11:46:11 +00:00
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
ValueT next ( ) {
if ( eof ( ) ) {
throw std : : runtime_error ( " unexpected end of arguments " ) ;
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
return * m_cur + + ;
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
private :
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
IterT m_cur ;
IterT m_begin ;
IterT m_end ;
} ;
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
struct TransferCommand {
2015-05-27 12:08:46 +00:00
const CryptoNote : : Currency & m_currency ;
2015-04-06 16:13:07 +00:00
size_t fake_outs_count ;
2015-05-27 12:08:46 +00:00
std : : vector < CryptoNote : : Transfer > dsts ;
2015-04-06 16:13:07 +00:00
std : : vector < uint8_t > extra ;
uint64_t fee ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
TransferCommand ( const CryptoNote : : Currency & currency ) :
2015-04-06 16:13:07 +00:00
m_currency ( currency ) , fake_outs_count ( 0 ) , fee ( currency . minimumFee ( ) ) {
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
bool parseArguments ( LoggerRef & logger , const std : : vector < std : : string > & args ) {
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
ArgumentReader < std : : vector < std : : string > : : const_iterator > ar ( args . begin ( ) , args . end ( ) ) ;
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
try {
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
auto mixin_str = ar . next ( ) ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
if ( ! Common : : fromString ( mixin_str , fake_outs_count ) ) {
logger ( ERROR , BRIGHT_RED ) < < " mixin_count should be non-negative integer, got " < < mixin_str ;
2015-04-06 16:13:07 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
while ( ! ar . eof ( ) ) {
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
auto arg = ar . next ( ) ;
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
if ( arg . size ( ) & & arg [ 0 ] = = ' - ' ) {
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
const auto & value = ar . next ( ) ;
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
if ( arg = = " -p " ) {
if ( ! createTxExtraWithPaymentId ( value , extra ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " payment ID has invalid format: \" " < < value < < " \" , expected 64-character string " ;
2015-04-06 16:13:07 +00:00
return false ;
2014-08-13 10:51:37 +00:00
}
2015-04-06 16:13:07 +00:00
} else if ( arg = = " -f " ) {
bool ok = m_currency . parseAmount ( value , fee ) ;
if ( ! ok ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Fee value is invalid: " < < value ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-04-06 16:13:07 +00:00
if ( fee < m_currency . minimumFee ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Fee value is less than minimum: " < < m_currency . minimumFee ( ) ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-04-06 16:13:07 +00:00
}
} else {
Transfer destination ;
2015-05-27 12:08:46 +00:00
CryptoNote : : tx_destination_entry de ;
2015-04-06 16:13:07 +00:00
if ( ! m_currency . parseAccountAddressString ( arg , de . addr ) ) {
crypto : : hash paymentId ;
2015-05-27 12:08:46 +00:00
if ( CryptoNote : : parsePaymentId ( arg , paymentId ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Invalid payment ID usage. Please, use -p <payment_id>. See help for details. " ;
2015-04-06 16:13:07 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Wrong address: " < < arg ;
2015-04-06 16:13:07 +00:00
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
return false ;
2014-08-13 10:51:37 +00:00
}
2015-04-06 16:13:07 +00:00
auto value = ar . next ( ) ;
bool ok = m_currency . parseAmount ( value , de . amount ) ;
if ( ! ok | | 0 = = de . amount ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " amount is wrong: " < < arg < < ' ' < < value < <
2015-04-06 16:13:07 +00:00
" , expected number from 0 to " < < m_currency . formatAmount ( std : : numeric_limits < uint64_t > : : max ( ) ) ;
return false ;
}
destination . address = arg ;
destination . amount = de . amount ;
dsts . push_back ( destination ) ;
2014-08-13 10:51:37 +00:00
}
2015-04-06 16:13:07 +00:00
}
if ( dsts . empty ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " At least one destination address is required " ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-04-06 16:13:07 +00:00
} catch ( const std : : exception & e ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < e . what ( ) ;
2015-04-06 16:13:07 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
2015-04-06 16:13:07 +00:00
return true ;
}
} ;
2015-05-27 12:08:46 +00:00
JsonValue buildLoggerConfiguration ( Level level , const std : : string & logfile ) {
JsonValue loggerConfiguration ( JsonValue : : OBJECT ) ;
loggerConfiguration . insert ( " globalLevel " , static_cast < int64_t > ( level ) ) ;
JsonValue & cfgLoggers = loggerConfiguration . insert ( " loggers " , JsonValue : : ARRAY ) ;
JsonValue & consoleLogger = cfgLoggers . pushBack ( JsonValue : : OBJECT ) ;
consoleLogger . insert ( " type " , " console " ) ;
consoleLogger . insert ( " level " , static_cast < int64_t > ( TRACE ) ) ;
consoleLogger . insert ( " pattern " , " " ) ;
JsonValue & fileLogger = cfgLoggers . pushBack ( JsonValue : : OBJECT ) ;
fileLogger . insert ( " type " , " file " ) ;
fileLogger . insert ( " filename " , logfile ) ;
fileLogger . insert ( " level " , static_cast < int64_t > ( TRACE ) ) ;
return loggerConfiguration ;
}
2015-04-14 18:00:44 +00:00
std : : error_code initAndLoadWallet ( IWallet & wallet , std : : istream & walletFile , const std : : string & password ) {
WalletHelper : : InitWalletResultObserver initObserver ;
std : : future < std : : error_code > f_initError = initObserver . initResult . get_future ( ) ;
2015-05-27 12:08:46 +00:00
WalletHelper : : IWalletRemoveObserverGuard removeGuard ( wallet , initObserver ) ;
2015-04-14 18:00:44 +00:00
wallet . initAndLoad ( walletFile , password ) ;
auto initError = f_initError . get ( ) ;
return initError ;
}
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
std : : string tryToOpenWalletOrLoadKeysOrThrow ( LoggerRef & logger , std : : unique_ptr < IWallet > & wallet , const std : : string & walletFile , const std : : string & password ) {
2015-04-06 16:13:07 +00:00
std : : string keys_file , walletFileName ;
WalletHelper : : prepareFileNames ( walletFile , keys_file , walletFileName ) ;
boost : : system : : error_code ignore ;
bool keysExists = boost : : filesystem : : exists ( keys_file , ignore ) ;
bool walletExists = boost : : filesystem : : exists ( walletFileName , ignore ) ;
2015-04-14 18:00:44 +00:00
if ( ! walletExists & & ! keysExists & & boost : : filesystem : : exists ( walletFile , ignore ) ) {
auto replaceEc = tools : : replace_file ( walletFile , walletFileName ) ;
if ( replaceEc ) {
throw std : : runtime_error ( " failed to rename file ' " + walletFile + " ' to ' " + walletFileName + " ' " ) ;
}
walletExists = true ;
}
2015-04-06 16:13:07 +00:00
if ( walletExists ) {
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " Loading wallet... " ;
2015-04-06 16:13:07 +00:00
std : : ifstream walletFile ;
walletFile . open ( walletFileName , std : : ios_base : : binary | std : : ios_base : : in ) ;
2015-04-14 18:00:44 +00:00
if ( walletFile . fail ( ) ) {
throw std : : runtime_error ( " error opening wallet file ' " + walletFileName + " ' " ) ;
}
auto initError = initAndLoadWallet ( * wallet , walletFile , password ) ;
2015-04-06 16:13:07 +00:00
walletFile . close ( ) ;
if ( initError ) { //bad password, or legacy format
if ( keysExists ) {
std : : stringstream ss ;
2015-05-27 12:08:46 +00:00
CryptoNote : : importLegacyKeys ( keys_file , password , ss ) ;
2015-04-06 16:13:07 +00:00
boost : : filesystem : : rename ( keys_file , keys_file + " .back " ) ;
boost : : filesystem : : rename ( walletFileName , walletFileName + " .back " ) ;
2015-04-14 18:00:44 +00:00
initError = initAndLoadWallet ( * wallet , ss , password ) ;
2015-04-06 16:13:07 +00:00
if ( initError ) {
throw std : : runtime_error ( " failed to load wallet: " + initError . message ( ) ) ;
}
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " Storing wallet... " ;
try {
CryptoNote : : WalletHelper : : storeWallet ( * wallet , walletFileName ) ;
} catch ( std : : exception & e ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to store wallet: " < < e . what ( ) ;
2015-04-14 18:00:44 +00:00
throw std : : runtime_error ( " error saving wallet file ' " + walletFileName + " ' " ) ;
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN ) < < " Stored ok " ;
2015-04-06 16:13:07 +00:00
return walletFileName ;
} else { // no keys, wallet error loading
2015-04-14 18:00:44 +00:00
throw std : : runtime_error ( " can't load wallet file ' " + walletFileName + " ', check password " ) ;
2015-04-06 16:13:07 +00:00
}
} else { //new wallet ok
return walletFileName ;
}
2015-04-14 18:00:44 +00:00
} else if ( keysExists ) { //wallet not exists but keys presented
std : : stringstream ss ;
2015-05-27 12:08:46 +00:00
CryptoNote : : importLegacyKeys ( keys_file , password , ss ) ;
2015-04-14 18:00:44 +00:00
boost : : filesystem : : rename ( keys_file , keys_file + " .back " ) ;
2015-04-06 16:13:07 +00:00
2015-04-14 18:00:44 +00:00
WalletHelper : : InitWalletResultObserver initObserver ;
std : : future < std : : error_code > f_initError = initObserver . initResult . get_future ( ) ;
2015-05-27 12:08:46 +00:00
WalletHelper : : IWalletRemoveObserverGuard removeGuard ( * wallet , initObserver ) ;
2015-04-14 18:00:44 +00:00
wallet - > initAndLoad ( ss , password ) ;
auto initError = f_initError . get ( ) ;
2015-05-27 12:08:46 +00:00
removeGuard . removeObserver ( ) ;
2015-04-14 18:00:44 +00:00
if ( initError ) {
throw std : : runtime_error ( " failed to load wallet: " + initError . message ( ) ) ;
}
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " Storing wallet... " ;
try {
CryptoNote : : WalletHelper : : storeWallet ( * wallet , walletFileName ) ;
} catch ( std : : exception & e ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to store wallet: " < < e . what ( ) ;
2015-04-14 18:00:44 +00:00
throw std : : runtime_error ( " error saving wallet file ' " + walletFileName + " ' " ) ;
}
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN ) < < " Stored ok " ;
2015-04-14 18:00:44 +00:00
return walletFileName ;
} else { //no wallet no keys
throw std : : runtime_error ( " wallet file ' " + walletFileName + " ' is not found " ) ;
2015-04-06 16:13:07 +00:00
}
}
2015-05-27 12:08:46 +00:00
2014-03-20 11:46:11 +00:00
}
2014-03-03 22:07:58 +00:00
std : : string simple_wallet : : get_commands_str ( )
{
std : : stringstream ss ;
ss < < " Commands: " < < ENDL ;
2015-05-27 12:08:46 +00:00
std : : string usage = m_consoleHandler . getUsage ( ) ;
2014-03-03 22:07:58 +00:00
boost : : replace_all ( usage , " \n " , " \n " ) ;
usage . insert ( 0 , " " ) ;
ss < < usage < < ENDL ;
return ss . str ( ) ;
}
2014-03-20 11:46:11 +00:00
bool simple_wallet : : help ( const std : : vector < std : : string > & args /* = std::vector<std::string>()*/ )
2014-03-03 22:07:58 +00:00
{
2014-04-02 16:00:17 +00:00
success_msg_writer ( ) < < get_commands_str ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
bool simple_wallet : : exit ( const std : : vector < std : : string > & args ) {
m_consoleHandler . requestStop ( ) ;
return true ;
}
simple_wallet : : simple_wallet ( System : : Dispatcher & dispatcher , const CryptoNote : : Currency & currency , Logging : : LoggerManager & log )
2014-03-03 22:07:58 +00:00
: m_daemon_port ( 0 )
2014-08-13 10:51:37 +00:00
, m_currency ( currency )
2015-05-27 12:08:46 +00:00
, logManager ( log )
, m_dispatcher ( dispatcher )
, logger ( log , " simplewallet " )
2014-04-02 16:00:17 +00:00
, m_refresh_progress_reporter ( * this )
2015-04-06 16:13:07 +00:00
, m_initResultPromise ( nullptr )
2014-03-03 22:07:58 +00:00
{
2015-05-27 12:08:46 +00:00
m_consoleHandler . setHandler ( " start_mining " , boost : : bind ( & simple_wallet : : start_mining , this , _1 ) , " start_mining [<number_of_threads>] - Start mining in daemon " ) ;
m_consoleHandler . setHandler ( " stop_mining " , boost : : bind ( & simple_wallet : : stop_mining , this , _1 ) , " Stop mining in daemon " ) ;
//m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance");
m_consoleHandler . setHandler ( " balance " , boost : : bind ( & simple_wallet : : show_balance , this , _1 ) , " Show current wallet balance " ) ;
m_consoleHandler . setHandler ( " incoming_transfers " , boost : : bind ( & simple_wallet : : show_incoming_transfers , this , _1 ) , " Show incoming transfers " ) ;
m_consoleHandler . setHandler ( " list_transfers " , boost : : bind ( & simple_wallet : : listTransfers , this , _1 ) , " Show all known transfers " ) ;
m_consoleHandler . setHandler ( " payments " , boost : : bind ( & simple_wallet : : show_payments , this , _1 ) , " payments <payment_id_1> [<payment_id_2> ... <payment_id_N>] - Show payments <payment_id_1>, ... <payment_id_N> " ) ;
m_consoleHandler . setHandler ( " bc_height " , boost : : bind ( & simple_wallet : : show_blockchain_height , this , _1 ) , " Show blockchain height " ) ;
m_consoleHandler . setHandler ( " transfer " , boost : : bind ( & simple_wallet : : transfer , this , _1 ) ,
2014-08-13 10:51:37 +00:00
" transfer <mixin_count> <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [-p payment_id] [-f fee] "
" - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. "
" <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available) " ) ;
2015-05-27 12:08:46 +00:00
m_consoleHandler . setHandler ( " set_log " , boost : : bind ( & simple_wallet : : set_log , this , _1 ) , " set_log <level> - Change current log detalization level, <level> is a number 0-4 " ) ;
m_consoleHandler . setHandler ( " address " , boost : : bind ( & simple_wallet : : print_address , this , _1 ) , " Show current wallet public address " ) ;
m_consoleHandler . setHandler ( " save " , boost : : bind ( & simple_wallet : : save , this , _1 ) , " Save wallet synchronized data " ) ;
m_consoleHandler . setHandler ( " reset " , boost : : bind ( & simple_wallet : : reset , this , _1 ) , " Discard cache data and start synchronizing from the start " ) ;
m_consoleHandler . setHandler ( " help " , boost : : bind ( & simple_wallet : : help , this , _1 ) , " Show this help " ) ;
m_consoleHandler . setHandler ( " exit " , boost : : bind ( & simple_wallet : : exit , this , _1 ) , " Close wallet " ) ;
2014-03-03 22:07:58 +00:00
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : set_log ( const std : : vector < std : : string > & args )
{
2015-04-06 16:13:07 +00:00
if ( args . size ( ) ! = 1 )
2014-03-03 22:07:58 +00:00
{
2014-04-02 16:00:17 +00:00
fail_msg_writer ( ) < < " use: set_log <log_level_number_0-4> " ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-03-20 11:46:11 +00:00
uint16_t l = 0 ;
2015-05-27 12:08:46 +00:00
if ( ! Common : : fromString ( args [ 0 ] , l ) )
2014-03-03 22:07:58 +00:00
{
2014-04-02 16:00:17 +00:00
fail_msg_writer ( ) < < " wrong number format, use: set_log <log_level_number_0-4> " ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
if ( l > Logging : : TRACE )
2014-03-03 22:07:58 +00:00
{
2014-04-02 16:00:17 +00:00
fail_msg_writer ( ) < < " wrong number range, use: set_log <log_level_number_0-4> " ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
logManager . setMaxLevel ( static_cast < Logging : : Level > ( l ) ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2015-04-06 16:13:07 +00:00
bool simple_wallet : : init ( const boost : : program_options : : variables_map & vm )
2014-04-29 16:26:45 +00:00
{
2015-04-06 16:13:07 +00:00
handle_command_line ( vm ) ;
2014-04-29 16:26:45 +00:00
2015-04-14 18:00:44 +00:00
if ( ! m_daemon_address . empty ( ) & & ( ! m_daemon_host . empty ( ) | | 0 ! = m_daemon_port ) ) {
2015-04-06 16:13:07 +00:00
fail_msg_writer ( ) < < " you can't specify daemon host or port several times " ;
return false ;
}
2014-04-29 16:26:45 +00:00
2015-04-06 16:13:07 +00:00
if ( m_generate_new . empty ( ) & & m_wallet_file_arg . empty ( ) ) {
std : : cout < < " Nor 'generate-new-wallet' neither 'wallet-file' argument was specified. \n What do you want to do? \n [O]pen existing wallet, [G]enerate new wallet file or [E]xit. \n " ;
char c ;
do {
std : : string answer ;
std : : getline ( std : : cin , answer ) ;
c = answer [ 0 ] ;
if ( ! ( c = = ' O ' | | c = = ' G ' | | c = = ' E ' | | c = = ' o ' | | c = = ' g ' | | c = = ' e ' ) ) {
2015-04-14 18:00:44 +00:00
std : : cout < < " Unknown command: " < < c < < std : : endl ;
2015-04-06 16:13:07 +00:00
} else {
break ;
}
} while ( true ) ;
2014-04-29 16:26:45 +00:00
2015-04-06 16:13:07 +00:00
if ( c = = ' E ' | | c = = ' e ' ) {
return false ;
}
2014-04-29 16:26:45 +00:00
2015-04-06 16:13:07 +00:00
std : : cout < < " Specify wallet file name (e.g., wallet.bin). \n " ;
2015-04-14 18:00:44 +00:00
std : : string userInput ;
do {
std : : cout < < " Wallet file name: " ;
std : : getline ( std : : cin , userInput ) ;
2015-05-27 12:08:46 +00:00
boost : : algorithm : : trim ( userInput ) ;
2015-04-14 18:00:44 +00:00
} while ( userInput . empty ( ) ) ;
2015-04-06 16:13:07 +00:00
if ( c = = ' g ' | | c = = ' G ' ) {
2015-04-14 18:00:44 +00:00
m_generate_new = userInput ;
2015-04-06 16:13:07 +00:00
} else {
2015-04-14 18:00:44 +00:00
m_wallet_file_arg = userInput ;
2014-04-29 16:26:45 +00:00
}
}
2015-04-06 16:13:07 +00:00
if ( ! m_generate_new . empty ( ) & & ! m_wallet_file_arg . empty ( ) ) {
fail_msg_writer ( ) < < " you can't specify 'generate-new-wallet' and 'wallet-file' arguments simultaneously " ;
2014-04-02 16:00:17 +00:00
return false ;
}
2014-03-03 22:07:58 +00:00
2015-04-14 18:00:44 +00:00
std : : string walletFileName ;
2015-04-06 16:13:07 +00:00
if ( ! m_generate_new . empty ( ) ) {
2015-04-14 18:00:44 +00:00
std : : string ignoredString ;
WalletHelper : : prepareFileNames ( m_generate_new , ignoredString , walletFileName ) ;
2015-04-06 16:13:07 +00:00
boost : : system : : error_code ignore ;
2015-04-14 18:00:44 +00:00
if ( boost : : filesystem : : exists ( walletFileName , ignore ) ) {
2015-04-06 16:13:07 +00:00
fail_msg_writer ( ) < < walletFileName < < " already exists " ;
2014-04-29 16:26:45 +00:00
return false ;
2015-04-06 16:13:07 +00:00
}
2014-04-02 16:00:17 +00:00
}
2014-03-03 22:07:58 +00:00
if ( m_daemon_host . empty ( ) )
m_daemon_host = " localhost " ;
if ( ! m_daemon_port )
m_daemon_port = RPC_DEFAULT_PORT ;
2015-05-27 12:08:46 +00:00
if ( ! m_daemon_address . empty ( ) ) {
if ( ! parseUrlAddress ( m_daemon_address , m_daemon_host , m_daemon_port ) ) {
fail_msg_writer ( ) < < " failed to parse daemon address: " < < m_daemon_address ;
return false ;
}
} else {
2014-04-02 16:00:17 +00:00
m_daemon_address = std : : string ( " http:// " ) + m_daemon_host + " : " + std : : to_string ( m_daemon_port ) ;
2015-05-27 12:08:46 +00:00
}
2014-03-03 22:07:58 +00:00
tools : : password_container pwd_container ;
2015-05-27 12:08:46 +00:00
if ( command_line : : has_arg ( vm , arg_password ) ) {
2014-03-03 22:07:58 +00:00
pwd_container . password ( command_line : : get_arg ( vm , arg_password ) ) ;
2015-05-27 12:08:46 +00:00
} else if ( ! pwd_container . read_password ( ) ) {
fail_msg_writer ( ) < < " failed to read wallet password " ;
return false ;
2014-03-03 22:07:58 +00:00
}
2015-04-06 16:13:07 +00:00
this - > m_node . reset ( new NodeRpcProxy ( m_daemon_host , m_daemon_port ) ) ;
std : : promise < std : : error_code > errorPromise ;
std : : future < std : : error_code > f_error = errorPromise . get_future ( ) ;
auto callback = [ & errorPromise ] ( std : : error_code e ) { errorPromise . set_value ( e ) ; } ;
m_node - > init ( callback ) ;
auto error = f_error . get ( ) ;
if ( error ) {
fail_msg_writer ( ) < < " failed to init NodeRPCProxy: " < < error . message ( ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
if ( ! m_generate_new . empty ( ) )
{
2015-04-06 16:13:07 +00:00
bool r = new_wallet ( walletFileName , pwd_container . password ( ) ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( r ) ) { logger ( ERROR , BRIGHT_RED ) < < " account creation failed " ; return false ; }
2014-03-03 22:07:58 +00:00
}
else
{
2015-04-06 16:13:07 +00:00
m_wallet . reset ( new Wallet ( m_currency , * m_node ) ) ;
try {
2015-05-27 12:08:46 +00:00
m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow ( logger , m_wallet , m_wallet_file_arg , pwd_container . password ( ) ) ;
2015-04-06 16:13:07 +00:00
} catch ( const std : : exception & e ) {
fail_msg_writer ( ) < < " failed to load wallet: " < < e . what ( ) ;
return false ;
}
m_wallet - > addObserver ( this ) ;
m_node - > addObserver ( this ) ;
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " Opened wallet: " < < m_wallet - > getAddress ( ) ;
2015-04-06 16:13:07 +00:00
success_msg_writer ( ) < <
" ********************************************************************** \n " < <
" Use \" help \" command to see the list of available commands. \n " < <
" ********************************************************************** " ;
2014-03-03 22:07:58 +00:00
}
return true ;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : deinit ( )
{
if ( ! m_wallet . get ( ) )
return true ;
2015-05-27 12:08:46 +00:00
return close_wallet ( ) ;
2014-03-03 22:07:58 +00:00
}
//----------------------------------------------------------------------------------------------------
2014-04-02 16:00:17 +00:00
void simple_wallet : : handle_command_line ( const boost : : program_options : : variables_map & vm )
2014-03-03 22:07:58 +00:00
{
2015-04-06 16:13:07 +00:00
m_wallet_file_arg = command_line : : get_arg ( vm , arg_wallet_file ) ;
m_generate_new = command_line : : get_arg ( vm , arg_generate_new_wallet ) ;
2014-03-03 22:07:58 +00:00
m_daemon_address = command_line : : get_arg ( vm , arg_daemon_address ) ;
2015-04-06 16:13:07 +00:00
m_daemon_host = command_line : : get_arg ( vm , arg_daemon_host ) ;
m_daemon_port = command_line : : get_arg ( vm , arg_daemon_port ) ;
2014-03-03 22:07:58 +00:00
}
//----------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool simple_wallet : : new_wallet ( const std : : string & wallet_file , const std : : string & password )
2014-03-03 22:07:58 +00:00
{
m_wallet_file = wallet_file ;
2015-04-06 16:13:07 +00:00
m_wallet . reset ( new Wallet ( m_currency , * m_node . get ( ) ) ) ;
m_node - > addObserver ( this ) ;
m_wallet - > addObserver ( this ) ;
2014-04-02 16:00:17 +00:00
try
{
2015-04-06 16:13:07 +00:00
m_initResultPromise . reset ( new std : : promise < std : : error_code > ( ) ) ;
std : : future < std : : error_code > f_initError = m_initResultPromise - > get_future ( ) ;
m_wallet - > initAndGenerate ( password ) ;
auto initError = f_initError . get ( ) ;
m_initResultPromise . reset ( nullptr ) ;
if ( initError ) {
fail_msg_writer ( ) < < " failed to generate new wallet: " < < initError . message ( ) ;
return false ;
}
2015-05-27 12:08:46 +00:00
try {
CryptoNote : : WalletHelper : : storeWallet ( * m_wallet , m_wallet_file ) ;
} catch ( std : : exception & e ) {
fail_msg_writer ( ) < < " failed to save new wallet: " < < e . what ( ) ;
throw ;
2015-04-06 16:13:07 +00:00
}
WalletAccountKeys keys ;
m_wallet - > getAccountKeys ( keys ) ;
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
2015-04-06 16:13:07 +00:00
" Generated new wallet: " < < m_wallet - > getAddress ( ) < < std : : endl < <
2015-05-27 12:08:46 +00:00
" view key: " < < Common : : podToHex ( keys . viewSecretKey ) ;
2014-04-02 16:00:17 +00:00
}
catch ( const std : : exception & e )
{
fail_msg_writer ( ) < < " failed to generate new wallet: " < < e . what ( ) ;
2014-03-03 22:07:58 +00:00
return false ;
2014-04-02 16:00:17 +00:00
}
2014-03-03 22:07:58 +00:00
2014-04-02 16:00:17 +00:00
success_msg_writer ( ) < <
" ********************************************************************** \n " < <
" Your wallet has been generated. \n " < <
" Use \" help \" command to see the list of available commands. \n " < <
" Always use \" exit \" command when closing simplewallet to save \n " < <
" current session's state. Otherwise, you will possibly need to synchronize \n " < <
" your wallet again. Your wallet key is NOT under risk anyway. \n " < <
" ********************************************************************** " ;
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : close_wallet ( )
{
2014-04-02 16:00:17 +00:00
try
{
2015-05-27 12:08:46 +00:00
CryptoNote : : WalletHelper : : storeWallet ( * m_wallet , m_wallet_file ) ;
2014-04-02 16:00:17 +00:00
}
catch ( const std : : exception & e )
{
fail_msg_writer ( ) < < e . what ( ) ;
return false ;
}
2015-05-27 12:08:46 +00:00
2015-04-06 16:13:07 +00:00
m_wallet - > removeObserver ( this ) ;
2015-05-27 12:08:46 +00:00
m_wallet - > shutdown ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : save ( const std : : vector < std : : string > & args )
{
2014-04-02 16:00:17 +00:00
try
{
2015-05-27 12:08:46 +00:00
CryptoNote : : WalletHelper : : storeWallet ( * m_wallet , m_wallet_file ) ;
2014-04-02 16:00:17 +00:00
success_msg_writer ( ) < < " Wallet data saved " ;
}
catch ( const std : : exception & e )
{
fail_msg_writer ( ) < < e . what ( ) ;
}
2014-03-03 22:07:58 +00:00
return true ;
}
2014-08-25 14:35:07 +00:00
bool simple_wallet : : reset ( const std : : vector < std : : string > & args ) {
m_wallet - > reset ( ) ;
success_msg_writer ( true ) < < " Reset is complete successfully " ;
return true ;
}
2014-03-20 11:46:11 +00:00
bool simple_wallet : : start_mining ( const std : : vector < std : : string > & args )
2014-03-03 22:07:58 +00:00
{
COMMAND_RPC_START_MINING : : request req ;
2015-04-06 16:13:07 +00:00
req . miner_address = m_wallet - > getAddress ( ) ;
2014-03-20 11:46:11 +00:00
2014-04-07 15:02:15 +00:00
bool ok = true ;
size_t max_mining_threads_count = ( std : : max ) ( std : : thread : : hardware_concurrency ( ) , static_cast < unsigned > ( 2 ) ) ;
2014-03-20 11:46:11 +00:00
if ( 0 = = args . size ( ) )
2014-03-03 22:07:58 +00:00
{
2014-03-20 11:46:11 +00:00
req . threads_count = 1 ;
}
else if ( 1 = = args . size ( ) )
{
2014-06-25 17:21:42 +00:00
uint16_t num = 1 ;
2015-05-27 12:08:46 +00:00
ok = Common : : fromString ( args [ 0 ] , num ) ;
2014-06-25 17:21:42 +00:00
ok = ok & & ( 1 < = num & & num < = max_mining_threads_count ) ;
2014-03-20 11:46:11 +00:00
req . threads_count = num ;
}
else
{
2014-04-07 15:02:15 +00:00
ok = false ;
}
if ( ! ok )
{
fail_msg_writer ( ) < < " invalid arguments. Please use start_mining [<number_of_threads>], " < <
" <number_of_threads> should be from 1 to " < < max_mining_threads_count ;
2014-03-20 11:46:11 +00:00
return true ;
2014-03-03 22:07:58 +00:00
}
2014-03-20 11:46:11 +00:00
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
COMMAND_RPC_START_MINING : : response res ;
2015-05-27 12:08:46 +00:00
try {
HttpClient httpClient ( m_dispatcher , m_daemon_host , m_daemon_port ) ;
invokeJsonCommand ( httpClient , " /start_mining " , req , res ) ;
std : : string err = interpret_rpc_response ( true , res . status ) ;
if ( err . empty ( ) )
success_msg_writer ( ) < < " Mining started in daemon " ;
else
fail_msg_writer ( ) < < " mining has NOT been started: " < < err ;
} catch ( const std : : exception & e ) {
fail_msg_writer ( ) < < " Failed to invoke rpc method: " < < e . what ( ) ;
}
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2014-03-20 11:46:11 +00:00
bool simple_wallet : : stop_mining ( const std : : vector < std : : string > & args )
2014-03-03 22:07:58 +00:00
{
COMMAND_RPC_STOP_MINING : : request req ;
COMMAND_RPC_STOP_MINING : : response res ;
2015-05-27 12:08:46 +00:00
try {
HttpClient httpClient ( m_dispatcher , m_daemon_host , m_daemon_port ) ;
invokeJsonCommand ( httpClient , " /stop_mining " , req , res ) ;
std : : string err = interpret_rpc_response ( true , res . status ) ;
if ( err . empty ( ) )
success_msg_writer ( ) < < " Mining stopped in daemon " ;
else
fail_msg_writer ( ) < < " mining has NOT been stopped: " < < err ;
} catch ( const std : : exception & e ) {
fail_msg_writer ( ) < < " Failed to invoke rpc method: " < < e . what ( ) ;
}
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2015-04-06 16:13:07 +00:00
void simple_wallet : : initCompleted ( std : : error_code result ) {
if ( m_initResultPromise . get ( ) ! = nullptr ) {
m_initResultPromise - > set_value ( result ) ;
}
2014-04-02 16:00:17 +00:00
}
//----------------------------------------------------------------------------------------------------
2015-04-06 16:13:07 +00:00
void simple_wallet : : localBlockchainUpdated ( uint64_t height )
2014-04-29 16:26:45 +00:00
{
2015-04-06 16:13:07 +00:00
m_refresh_progress_reporter . update ( height , false ) ;
2014-04-29 16:26:45 +00:00
}
//----------------------------------------------------------------------------------------------------
2015-04-06 16:13:07 +00:00
void simple_wallet : : externalTransactionCreated ( CryptoNote : : TransactionId transactionId )
2014-03-03 22:07:58 +00:00
{
2015-04-06 16:13:07 +00:00
TransactionInfo txInfo ;
m_wallet - > getTransaction ( transactionId , txInfo ) ;
2014-04-02 16:00:17 +00:00
2015-04-08 15:08:54 +00:00
if ( txInfo . totalAmount > = 0 ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , GREEN ) < <
" Height " < < txInfo . blockHeight < < " , transaction " < < Common : : podToHex ( txInfo . hash ) < <
" , received " < < m_currency . formatAmount ( txInfo . totalAmount ) ;
2015-04-08 15:08:54 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger ( INFO , MAGENTA ) < <
" Height " < < txInfo . blockHeight < < " , transaction " < < Common : : podToHex ( txInfo . hash ) < <
2015-04-08 15:08:54 +00:00
" , spent " < < m_currency . formatAmount ( static_cast < uint64_t > ( - txInfo . totalAmount ) ) ;
}
2015-05-27 12:08:46 +00:00
2015-04-06 16:13:07 +00:00
m_refresh_progress_reporter . update ( txInfo . blockHeight , true ) ;
2014-03-03 22:07:58 +00:00
}
//----------------------------------------------------------------------------------------------------
2014-03-20 11:46:11 +00:00
bool simple_wallet : : show_balance ( const std : : vector < std : : string > & args /* = std::vector<std::string>()*/ )
2014-03-03 22:07:58 +00:00
{
2015-04-08 15:08:54 +00:00
success_msg_writer ( ) < < " available balance: " < < m_currency . formatAmount ( m_wallet - > actualBalance ( ) ) < <
" , locked amount: " < < m_currency . formatAmount ( m_wallet - > pendingBalance ( ) ) ;
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2014-03-20 11:46:11 +00:00
bool simple_wallet : : show_incoming_transfers ( const std : : vector < std : : string > & args )
2014-03-03 22:07:58 +00:00
{
2015-04-06 16:13:07 +00:00
bool hasTransfers = false ;
size_t transactionsCount = m_wallet - > getTransactionCount ( ) ;
for ( size_t trantransactionNumber = 0 ; trantransactionNumber < transactionsCount ; + + trantransactionNumber ) {
TransactionInfo txInfo ;
m_wallet - > getTransaction ( trantransactionNumber , txInfo ) ;
if ( txInfo . totalAmount < 0 ) continue ;
hasTransfers = true ;
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " amount \t tx id " ;
logger ( INFO , GREEN ) < < // spent - magenta
std : : setw ( 21 ) < < m_currency . formatAmount ( txInfo . totalAmount ) < < ' \t ' < < Common : : podToHex ( txInfo . hash ) ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
if ( ! hasTransfers ) success_msg_writer ( ) < < " No incoming transfers " ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-08-25 14:35:07 +00:00
bool simple_wallet : : listTransfers ( const std : : vector < std : : string > & args ) {
2015-04-06 16:13:07 +00:00
size_t transactionsCount = m_wallet - > getTransactionCount ( ) ;
for ( size_t trantransactionNumber = 0 ; trantransactionNumber < transactionsCount ; + + trantransactionNumber ) {
TransactionInfo txInfo ;
m_wallet - > getTransaction ( trantransactionNumber , txInfo ) ;
if ( txInfo . state ! = TransactionState : : Active ) {
continue ;
}
std : : string paymentIdStr = " " ;
std : : vector < uint8_t > extraVec ;
extraVec . reserve ( txInfo . extra . size ( ) ) ;
std : : for_each ( txInfo . extra . begin ( ) , txInfo . extra . end ( ) , [ & extraVec ] ( const char el ) { extraVec . push_back ( el ) ; } ) ;
crypto : : hash paymentId ;
2015-05-27 12:08:46 +00:00
paymentIdStr = ( getPaymentIdFromTxExtra ( extraVec , paymentId ) & & paymentId ! = null_hash ? Common : : podToHex ( paymentId ) : " " ) ;
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
std : : string address = " - " ;
2015-04-06 16:13:07 +00:00
if ( txInfo . totalAmount < 0 ) {
if ( txInfo . transferCount > 0 )
{
Transfer tr ;
m_wallet - > getTransfer ( txInfo . firstTransferId , tr ) ;
address = tr . address ;
}
2014-08-25 14:35:07 +00:00
}
2015-05-27 12:08:46 +00:00
logger ( INFO , txInfo . totalAmount < 0 ? MAGENTA : GREEN )
2015-04-06 16:13:07 +00:00
< < txInfo . timestamp
< < " , " < < ( txInfo . totalAmount < 0 ? " OUTPUT " : " INPUT " )
2015-05-27 12:08:46 +00:00
< < " , " < < Common : : podToHex ( txInfo . hash )
< < " , " < < ( txInfo . totalAmount < 0 ? " - " : " " ) < < m_currency . formatAmount ( std : : abs ( txInfo . totalAmount ) )
2015-04-06 16:13:07 +00:00
< < " , " < < m_currency . formatAmount ( txInfo . fee )
2015-05-27 12:08:46 +00:00
< < " , " < < ( paymentIdStr . empty ( ) ? std : : string ( " - " ) : paymentIdStr )
2014-08-25 14:35:07 +00:00
< < " , " < < address
2015-04-06 16:13:07 +00:00
< < " , " < < txInfo . blockHeight
< < " , " < < txInfo . unlockTime ;
2014-08-25 14:35:07 +00:00
}
2015-04-06 16:13:07 +00:00
if ( transactionsCount = = 0 ) success_msg_writer ( ) < < " No transfers " ;
2014-08-25 14:35:07 +00:00
return true ;
}
2014-04-29 16:26:45 +00:00
bool simple_wallet : : show_payments ( const std : : vector < std : : string > & args )
{
2015-04-06 16:13:07 +00:00
if ( args . empty ( ) )
2014-04-29 16:26:45 +00:00
{
2014-08-25 14:35:07 +00:00
fail_msg_writer ( ) < < " expected at least one payment ID " ;
2014-04-29 16:26:45 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " payment \t " < <
2014-04-29 16:26:45 +00:00
" transaction \t " < <
2015-04-06 16:13:07 +00:00
" height \t amount " ;
2014-04-29 16:26:45 +00:00
bool payments_found = false ;
2015-04-06 16:13:07 +00:00
for ( const std : : string & arg : args )
2014-04-29 16:26:45 +00:00
{
2015-04-06 16:13:07 +00:00
crypto : : hash expectedPaymentId ;
2015-05-27 12:08:46 +00:00
if ( CryptoNote : : parsePaymentId ( arg , expectedPaymentId ) )
2014-04-29 16:26:45 +00:00
{
2015-04-06 16:13:07 +00:00
size_t transactionsCount = m_wallet - > getTransactionCount ( ) ;
for ( size_t trantransactionNumber = 0 ; trantransactionNumber < transactionsCount ; + + trantransactionNumber ) {
TransactionInfo txInfo ;
m_wallet - > getTransaction ( trantransactionNumber , txInfo ) ;
if ( txInfo . totalAmount < 0 ) continue ;
std : : vector < uint8_t > extraVec ;
extraVec . reserve ( txInfo . extra . size ( ) ) ;
std : : for_each ( txInfo . extra . begin ( ) , txInfo . extra . end ( ) , [ & extraVec ] ( const char el ) { extraVec . push_back ( el ) ; } ) ;
crypto : : hash paymentId ;
2015-05-27 12:08:46 +00:00
if ( CryptoNote : : getPaymentIdFromTxExtra ( extraVec , paymentId ) & & paymentId = = expectedPaymentId ) {
2014-04-29 16:26:45 +00:00
payments_found = true ;
2015-04-06 16:13:07 +00:00
success_msg_writer ( true ) < <
paymentId < < " \t \t " < <
2015-05-27 12:08:46 +00:00
Common : : podToHex ( txInfo . hash ) < <
2015-04-06 16:13:07 +00:00
std : : setw ( 8 ) < < txInfo . blockHeight < < ' \t ' < <
std : : setw ( 21 ) < < m_currency . formatAmount ( txInfo . totalAmount ) ; // << '\t' <<
2014-04-29 16:26:45 +00:00
}
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
if ( ! payments_found ) {
2015-04-06 16:13:07 +00:00
success_msg_writer ( ) < < " No payments with id " < < expectedPaymentId ;
continue ;
2014-04-29 16:26:45 +00:00
}
2015-05-27 12:08:46 +00:00
} else {
2014-08-25 14:35:07 +00:00
fail_msg_writer ( ) < < " payment ID has invalid format: \" " < < arg < < " \" , expected 64-character string " ;
2014-04-29 16:26:45 +00:00
}
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2014-03-20 11:46:11 +00:00
bool simple_wallet : : show_blockchain_height ( const std : : vector < std : : string > & args )
2014-03-03 22:07:58 +00:00
{
2015-04-06 16:13:07 +00:00
try {
uint64_t bc_height = m_node - > getLastLocalBlockHeight ( ) ;
2014-04-02 16:00:17 +00:00
success_msg_writer ( ) < < bc_height ;
2015-04-06 16:13:07 +00:00
} catch ( std : : exception & e ) {
fail_msg_writer ( ) < < " failed to get blockchain height: " < < e . what ( ) ;
}
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2014-08-13 10:51:37 +00:00
bool simple_wallet : : transfer ( const std : : vector < std : : string > & args )
2014-03-03 22:07:58 +00:00
{
2014-08-13 10:51:37 +00:00
try
2014-04-29 16:26:45 +00:00
{
2014-08-13 10:51:37 +00:00
TransferCommand cmd ( m_currency ) ;
2014-03-20 11:46:11 +00:00
2015-05-27 12:08:46 +00:00
if ( ! cmd . parseArguments ( logger , args ) )
2015-04-06 16:13:07 +00:00
return false ;
2015-05-27 12:08:46 +00:00
CryptoNote : : WalletHelper : : SendCompleteResultObserver sent ;
2015-04-06 16:13:07 +00:00
std : : string extraString ;
std : : copy ( cmd . extra . begin ( ) , cmd . extra . end ( ) , std : : back_inserter ( extraString ) ) ;
2015-05-27 12:08:46 +00:00
WalletHelper : : IWalletRemoveObserverGuard removeGuard ( * m_wallet , sent ) ;
2015-04-06 16:13:07 +00:00
CryptoNote : : TransactionId tx = m_wallet - > sendTransaction ( cmd . dsts , cmd . fee , extraString , cmd . fake_outs_count , 0 ) ;
if ( tx = = INVALID_TRANSACTION_ID ) {
fail_msg_writer ( ) < < " Can't send money " ;
return true ;
}
2015-05-27 12:08:46 +00:00
std : : error_code sendError = sent . wait ( tx ) ;
removeGuard . removeObserver ( ) ;
2015-04-06 16:13:07 +00:00
if ( sendError ) {
fail_msg_writer ( ) < < sendError . message ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
2015-04-06 16:13:07 +00:00
}
2014-03-03 22:07:58 +00:00
2015-04-06 16:13:07 +00:00
CryptoNote : : TransactionInfo txInfo ;
m_wallet - > getTransaction ( tx , txInfo ) ;
2015-05-27 12:08:46 +00:00
success_msg_writer ( true ) < < " Money successfully sent, transaction " < < Common : : podToHex ( txInfo . hash ) ;
2014-08-25 14:35:07 +00:00
try {
2015-05-27 12:08:46 +00:00
CryptoNote : : WalletHelper : : storeWallet ( * m_wallet , m_wallet_file ) ;
2014-08-25 14:35:07 +00:00
} catch ( const std : : exception & e ) {
fail_msg_writer ( ) < < e . what ( ) ;
2015-04-06 16:13:07 +00:00
return true ;
2014-04-02 16:00:17 +00:00
}
}
2015-04-06 16:13:07 +00:00
catch ( const std : : system_error & e )
2014-04-02 16:00:17 +00:00
{
2015-04-06 16:13:07 +00:00
fail_msg_writer ( ) < < " unexpected error: " < < e . what ( ) ;
2014-04-02 16:00:17 +00:00
}
catch ( const std : : exception & e )
{
fail_msg_writer ( ) < < " unexpected error: " < < e . what ( ) ;
}
catch ( . . . )
{
fail_msg_writer ( ) < < " unknown error " ;
2014-03-03 22:07:58 +00:00
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool simple_wallet : : run ( ) {
2015-04-06 16:13:07 +00:00
std : : string addr_start = m_wallet - > getAddress ( ) . substr ( 0 , 6 ) ;
2015-05-27 12:08:46 +00:00
m_consoleHandler . start ( false , " [wallet " + addr_start + " ]: " , Common : : Console : : Color : : BrightYellow ) ;
return true ;
2014-03-20 11:46:11 +00:00
}
//----------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
void simple_wallet : : stop ( ) {
m_consoleHandler . requestStop ( ) ;
2014-03-03 22:07:58 +00:00
}
//----------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool simple_wallet : : print_address ( const std : : vector < std : : string > & args /* = std::vector<std::string>()*/ ) {
2015-04-06 16:13:07 +00:00
success_msg_writer ( ) < < m_wallet - > getAddress ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool simple_wallet : : process_command ( const std : : vector < std : : string > & args ) {
return m_consoleHandler . runCommand ( args ) ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
2015-04-06 16:13:07 +00:00
2014-03-03 22:07:58 +00:00
int main ( int argc , char * argv [ ] )
{
# ifdef WIN32
2015-04-06 16:13:07 +00:00
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) ;
2014-03-03 22:07:58 +00:00
# endif
po : : options_description desc_general ( " General options " ) ;
command_line : : add_arg ( desc_general , command_line : : arg_help ) ;
command_line : : add_arg ( desc_general , command_line : : arg_version ) ;
po : : options_description desc_params ( " Wallet options " ) ;
command_line : : add_arg ( desc_params , arg_wallet_file ) ;
command_line : : add_arg ( desc_params , arg_generate_new_wallet ) ;
command_line : : add_arg ( desc_params , arg_password ) ;
command_line : : add_arg ( desc_params , arg_daemon_address ) ;
command_line : : add_arg ( desc_params , arg_daemon_host ) ;
command_line : : add_arg ( desc_params , arg_daemon_port ) ;
command_line : : add_arg ( desc_params , arg_command ) ;
command_line : : add_arg ( desc_params , arg_log_level ) ;
2014-08-13 10:51:37 +00:00
command_line : : add_arg ( desc_params , arg_testnet ) ;
2014-04-02 16:00:17 +00:00
tools : : wallet_rpc_server : : init_options ( desc_params ) ;
2014-03-03 22:07:58 +00:00
po : : positional_options_description positional_options ;
positional_options . add ( arg_command . name , - 1 ) ;
po : : options_description desc_all ;
desc_all . add ( desc_general ) . add ( desc_params ) ;
2015-05-27 12:08:46 +00:00
Logging : : LoggerManager logManager ;
Logging : : LoggerRef logger ( logManager , " simplewallet " ) ;
System : : Dispatcher dispatcher ;
2014-03-03 22:07:58 +00:00
po : : variables_map vm ;
2015-05-27 12:08:46 +00:00
bool r = command_line : : handle_error_helper ( desc_all , [ & ] ( ) {
2014-03-03 22:07:58 +00:00
po : : store ( command_line : : parse_command_line ( argc , argv , desc_general , true ) , vm ) ;
2015-05-27 12:08:46 +00:00
if ( command_line : : get_arg ( vm , command_line : : arg_help ) ) {
CryptoNote : : Currency tmp_currency = CryptoNote : : CurrencyBuilder ( logManager ) . currency ( ) ;
CryptoNote : : simple_wallet tmp_wallet ( dispatcher , tmp_currency , logManager ) ;
std : : cout < < CRYPTONOTE_NAME < < " wallet v " < < PROJECT_VERSION_LONG < < std : : endl ;
std : : cout < < " Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>] " ;
std : : cout < < desc_all < < ' \n ' < < tmp_wallet . get_commands_str ( ) ;
2014-03-03 22:07:58 +00:00
return false ;
2015-05-27 12:08:46 +00:00
} else if ( command_line : : get_arg ( vm , command_line : : arg_version ) ) {
std : : cout < < CRYPTONOTE_NAME < < " wallet v " < < PROJECT_VERSION_LONG ;
2014-03-03 22:07:58 +00:00
return false ;
}
auto parser = po : : command_line_parser ( argc , argv ) . options ( desc_params ) . positional ( positional_options ) ;
po : : store ( parser . run ( ) , vm ) ;
po : : notify ( vm ) ;
return true ;
} ) ;
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
if ( ! r )
return 1 ;
//set up logging options
2015-05-27 12:08:46 +00:00
Level logLevel = DEBUGGING ;
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
if ( command_line : : has_arg ( vm , arg_log_level ) ) {
logLevel = static_cast < Level > ( command_line : : get_arg ( vm , arg_log_level ) ) ;
2014-03-03 22:07:58 +00:00
}
2014-04-29 16:26:45 +00:00
2015-05-27 12:08:46 +00:00
logManager . configure ( buildLoggerConfiguration ( logLevel , Common : : ReplaceExtenstion ( argv [ 0 ] , " .log " ) ) ) ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < CRYPTONOTE_NAME < < " wallet v " < < PROJECT_VERSION_LONG ;
CryptoNote : : Currency currency = CryptoNote : : CurrencyBuilder ( logManager ) .
testnet ( command_line : : get_arg ( vm , arg_testnet ) ) . currency ( ) ;
if ( command_line : : has_arg ( vm , tools : : wallet_rpc_server : : arg_rpc_bind_port ) ) {
2014-04-29 16:26:45 +00:00
//runs wallet with rpc interface
2015-04-06 16:13:07 +00:00
if ( ! command_line : : has_arg ( vm , arg_wallet_file ) )
2014-04-02 16:00:17 +00:00
{
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Wallet file not set. " ;
2014-04-02 16:00:17 +00:00
return 1 ;
}
2015-04-06 16:13:07 +00:00
if ( ! command_line : : has_arg ( vm , arg_daemon_address ) )
2014-04-02 16:00:17 +00:00
{
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Daemon address not set. " ;
2014-04-02 16:00:17 +00:00
return 1 ;
}
2015-04-06 16:13:07 +00:00
if ( ! command_line : : has_arg ( vm , arg_password ) )
2014-04-02 16:00:17 +00:00
{
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Wallet password not set. " ;
2014-04-02 16:00:17 +00:00
return 1 ;
}
2014-03-03 22:07:58 +00:00
2015-04-06 16:13:07 +00:00
std : : string wallet_file = command_line : : get_arg ( vm , arg_wallet_file ) ;
2014-04-02 16:00:17 +00:00
std : : string wallet_password = command_line : : get_arg ( vm , arg_password ) ;
2015-04-06 16:13:07 +00:00
std : : string daemon_address = command_line : : get_arg ( vm , arg_daemon_address ) ;
2014-04-02 16:00:17 +00:00
std : : string daemon_host = command_line : : get_arg ( vm , arg_daemon_host ) ;
int daemon_port = command_line : : get_arg ( vm , arg_daemon_port ) ;
if ( daemon_host . empty ( ) )
daemon_host = " localhost " ;
if ( ! daemon_port )
daemon_port = RPC_DEFAULT_PORT ;
2015-05-27 12:08:46 +00:00
if ( ! daemon_address . empty ( ) ) {
uint16_t port = 0 ;
if ( ! parseUrlAddress ( daemon_address , daemon_host , port ) ) {
logger ( ERROR , BRIGHT_RED ) < < " failed to parse daemon address: " < < daemon_address ;
return 1 ;
}
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
daemon_port = port ;
}
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
std : : unique_ptr < INode > node ( new NodeRpcProxy ( daemon_host , daemon_port ) ) ;
2015-04-06 16:13:07 +00:00
std : : promise < std : : error_code > errorPromise ;
std : : future < std : : error_code > error = errorPromise . get_future ( ) ;
auto callback = [ & errorPromise ] ( std : : error_code e ) { errorPromise . set_value ( e ) ; } ;
node - > init ( callback ) ;
if ( error . get ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < ( " failed to init NodeRPCProxy " ) ;
2015-04-06 16:13:07 +00:00
return 1 ;
}
2015-05-27 12:08:46 +00:00
std : : unique_ptr < IWallet > wallet ( new Wallet ( currency , * node . get ( ) ) ) ;
2015-04-06 16:13:07 +00:00
std : : string walletFileName ;
2015-05-27 12:08:46 +00:00
try {
walletFileName = : : tryToOpenWalletOrLoadKeysOrThrow ( logger , wallet , wallet_file , wallet_password ) ;
logger ( INFO ) < < " available balance: " < < currency . formatAmount ( wallet - > actualBalance ( ) ) < <
" , locked amount: " < < currency . formatAmount ( wallet - > pendingBalance ( ) ) ;
logger ( INFO , BRIGHT_GREEN ) < < " Loaded ok " ;
} catch ( const std : : exception & e ) {
logger ( ERROR , BRIGHT_RED ) < < " Wallet initialize failed: " < < e . what ( ) ;
2014-04-02 16:00:17 +00:00
return 1 ;
}
2015-04-06 16:13:07 +00:00
2015-05-27 12:08:46 +00:00
tools : : wallet_rpc_server wrpc ( dispatcher , logManager , * wallet , * node , currency , walletFileName ) ;
if ( ! wrpc . init ( vm ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to initialize wallet rpc server " ;
return 1 ;
}
2014-04-02 16:00:17 +00:00
2015-04-06 16:13:07 +00:00
tools : : SignalHandler : : install ( [ & wrpc , & wallet ] {
2014-04-02 16:00:17 +00:00
wrpc . send_stop_signal ( ) ;
} ) ;
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " Starting wallet rpc server " ;
2014-04-02 16:00:17 +00:00
wrpc . run ( ) ;
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < " Stopped wallet rpc server " ;
try {
CryptoNote : : WalletHelper : : storeWallet ( * wallet , walletFileName ) ;
logger ( INFO , BRIGHT_GREEN ) < < " Stored ok " ;
2014-04-02 16:00:17 +00:00
}
catch ( const std : : exception & e )
{
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Failed to store wallet: " < < e . what ( ) ;
2014-04-02 16:00:17 +00:00
return 1 ;
}
2015-05-27 12:08:46 +00:00
} else {
2014-04-29 16:26:45 +00:00
//runs wallet with console interface
2015-05-27 12:08:46 +00:00
CryptoNote : : simple_wallet wal ( dispatcher , currency , logManager ) ;
if ( ! wal . init ( vm ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to initialize wallet " ;
return 1 ;
}
2014-03-20 11:46:11 +00:00
2014-04-02 16:00:17 +00:00
std : : vector < std : : string > command = command_line : : get_arg ( vm , arg_command ) ;
if ( ! command . empty ( ) )
2014-08-13 10:51:37 +00:00
wal . process_command ( command ) ;
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
tools : : SignalHandler : : install ( [ & wal ] {
wal . stop ( ) ;
2014-04-02 16:00:17 +00:00
} ) ;
2015-05-27 12:08:46 +00:00
2014-08-13 10:51:37 +00:00
wal . run ( ) ;
2014-03-03 22:07:58 +00:00
2015-04-06 16:13:07 +00:00
if ( ! wal . deinit ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Failed to close wallet " ;
} else {
logger ( INFO ) < < " Wallet closed " ;
}
2014-04-02 16:00:17 +00:00
}
2014-03-03 22:07:58 +00:00
return 1 ;
//CATCH_ENTRY_L0("main", 1);
}