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-04-02 16:00:17 +00:00
2015-07-30 15:22:07 +00:00
# include "WalletRpcServer.h"
2015-05-27 12:08:46 +00:00
# include <fstream>
2015-07-30 15:22:07 +00:00
# include "Common/CommandLine.h"
2015-05-27 12:08:46 +00:00
# include "Common/StringTools.h"
2015-07-30 15:22:07 +00:00
# include "CryptoNoteCore/CryptoNoteFormatUtils.h"
# include "CryptoNoteCore/Account.h"
2014-04-02 16:00:17 +00:00
# include "crypto/hash.h"
2015-07-30 15:22:07 +00:00
# include "WalletLegacy/WalletHelper.h"
2015-05-27 12:08:46 +00:00
// #include "wallet_errors.h"
2014-04-02 16:00:17 +00:00
2015-07-30 15:22:07 +00:00
# include "Rpc/JsonRpc.h"
2014-04-02 16:00:17 +00:00
2015-05-27 12:08:46 +00:00
using namespace Logging ;
2015-04-06 16:13:07 +00:00
using namespace CryptoNote ;
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
namespace Tools {
2015-05-27 12:08:46 +00:00
const command_line : : arg_descriptor < uint16_t > wallet_rpc_server : : arg_rpc_bind_port = { " rpc-bind-port " , " Starts wallet as rpc server for wallet operations, sets bind port for server " , 0 , true } ;
2015-04-06 16:13:07 +00:00
const command_line : : arg_descriptor < std : : string > wallet_rpc_server : : arg_rpc_bind_ip = { " rpc-bind-ip " , " Specify ip to bind rpc server " , " 127.0.0.1 " } ;
2014-04-02 16:00:17 +00:00
2015-04-06 16:13:07 +00:00
void wallet_rpc_server : : init_options ( boost : : program_options : : options_description & desc ) {
command_line : : add_arg ( desc , arg_rpc_bind_ip ) ;
command_line : : add_arg ( desc , arg_rpc_bind_port ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
wallet_rpc_server : : wallet_rpc_server (
System : : Dispatcher & dispatcher ,
Logging : : ILogger & log ,
2015-07-30 15:22:07 +00:00
CryptoNote : : IWalletLegacy & w ,
2015-05-27 12:08:46 +00:00
CryptoNote : : INode & n ,
CryptoNote : : Currency & currency ,
const std : : string & walletFile )
:
HttpServer ( dispatcher , log ) ,
logger ( log , " WalletRpc " ) ,
m_dispatcher ( dispatcher ) ,
m_stopComplete ( dispatcher ) ,
m_wallet ( w ) ,
m_node ( n ) ,
m_currency ( currency ) ,
m_walletFilename ( walletFile ) {
2015-04-06 16:13:07 +00:00
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : run ( ) {
2015-05-27 12:08:46 +00:00
start ( m_bind_ip , m_port ) ;
m_stopComplete . wait ( ) ;
return true ;
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
void wallet_rpc_server : : send_stop_signal ( ) {
m_dispatcher . remoteSpawn ( [ this ] {
std : : cout < < " wallet_rpc_server::send_stop_signal() " < < std : : endl ;
stop ( ) ;
m_stopComplete . set ( ) ;
} ) ;
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
2015-04-06 16:13:07 +00:00
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : handle_command_line ( const boost : : program_options : : variables_map & vm ) {
m_bind_ip = command_line : : get_arg ( vm , arg_rpc_bind_ip ) ;
m_port = command_line : : get_arg ( vm , arg_rpc_bind_port ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : init ( const boost : : program_options : : variables_map & vm ) {
2015-05-27 12:08:46 +00:00
if ( ! handle_command_line ( vm ) ) {
logger ( ERROR ) < < " Failed to process command line in wallet_rpc_server " ;
return false ;
}
return true ;
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
void wallet_rpc_server : : processRequest ( const CryptoNote : : HttpRequest & request , CryptoNote : : HttpResponse & response ) {
using namespace CryptoNote : : JsonRpc ;
JsonRpcRequest jsonRequest ;
JsonRpcResponse jsonResponse ;
2015-04-06 16:13:07 +00:00
try {
2015-05-27 12:08:46 +00:00
jsonRequest . parseRequest ( request . getBody ( ) ) ;
jsonResponse . setId ( jsonRequest . getId ( ) ) ;
static std : : unordered_map < std : : string , JsonMemberMethod > s_methods = {
{ " getbalance " , makeMemberMethod ( & wallet_rpc_server : : on_getbalance ) } ,
{ " transfer " , makeMemberMethod ( & wallet_rpc_server : : on_transfer ) } ,
{ " store " , makeMemberMethod ( & wallet_rpc_server : : on_store ) } ,
{ " get_payments " , makeMemberMethod ( & wallet_rpc_server : : on_get_payments ) } ,
{ " get_transfers " , makeMemberMethod ( & wallet_rpc_server : : on_get_transfers ) } ,
{ " get_height " , makeMemberMethod ( & wallet_rpc_server : : on_get_height ) } ,
{ " reset " , makeMemberMethod ( & wallet_rpc_server : : on_reset ) }
} ;
auto it = s_methods . find ( jsonRequest . getMethod ( ) ) ;
if ( it = = s_methods . end ( ) ) {
throw JsonRpcError ( errMethodNotFound ) ;
}
it - > second ( this , jsonRequest , jsonResponse ) ;
} catch ( const JsonRpcError & err ) {
jsonResponse . setError ( err ) ;
} catch ( const std : : exception & e ) {
jsonResponse . setError ( JsonRpcError ( WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR , e . what ( ) ) ) ;
2014-04-02 16:00:17 +00:00
}
2015-05-27 12:08:46 +00:00
response . setBody ( jsonResponse . getBody ( ) ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_getbalance ( const wallet_rpc : : COMMAND_RPC_GET_BALANCE : : request & req , wallet_rpc : : COMMAND_RPC_GET_BALANCE : : response & res ) {
2015-07-30 15:22:07 +00:00
res . locked_amount = m_wallet . pendingBalance ( ) ;
res . available_balance = m_wallet . actualBalance ( ) ;
2015-04-06 16:13:07 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_transfer ( const wallet_rpc : : COMMAND_RPC_TRANSFER : : request & req , wallet_rpc : : COMMAND_RPC_TRANSFER : : response & res ) {
2015-07-30 15:22:07 +00:00
std : : vector < CryptoNote : : WalletLegacyTransfer > transfers ;
2015-04-06 16:13:07 +00:00
for ( auto it = req . destinations . begin ( ) ; it ! = req . destinations . end ( ) ; it + + ) {
2015-07-30 15:22:07 +00:00
CryptoNote : : WalletLegacyTransfer transfer ;
2015-04-06 16:13:07 +00:00
transfer . address = it - > address ;
transfer . amount = it - > amount ;
transfers . push_back ( transfer ) ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
std : : vector < uint8_t > extra ;
if ( ! req . payment_id . empty ( ) ) {
std : : string payment_id_str = req . payment_id ;
2015-07-30 15:22:07 +00:00
Crypto : : Hash payment_id ;
2015-05-27 12:08:46 +00:00
if ( ! CryptoNote : : parsePaymentId ( payment_id_str , payment_id ) ) {
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ,
" Payment id has invalid format: \" " + payment_id_str + " \" , expected 64-character string " ) ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
2015-07-30 15:22:07 +00:00
BinaryArray extra_nonce ;
CryptoNote : : setPaymentIdToTransactionExtraNonce ( extra_nonce , payment_id ) ;
if ( ! CryptoNote : : addExtraNonceToTransactionExtra ( extra , extra_nonce ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ,
" Something went wrong with payment_id. Please check its format: \" " + payment_id_str + " \" , expected 64-character string " ) ;
2014-04-02 16:00:17 +00:00
}
}
2014-06-25 17:21:42 +00:00
2015-04-06 16:13:07 +00:00
std : : string extraString ;
std : : copy ( extra . begin ( ) , extra . end ( ) , std : : back_inserter ( extraString ) ) ;
try {
2015-05-27 12:08:46 +00:00
CryptoNote : : WalletHelper : : SendCompleteResultObserver sent ;
WalletHelper : : IWalletRemoveObserverGuard removeGuard ( m_wallet , sent ) ;
2014-06-25 17:21:42 +00:00
2015-04-06 16:13:07 +00:00
CryptoNote : : TransactionId tx = m_wallet . sendTransaction ( transfers , req . fee , extraString , req . mixin , req . unlock_time ) ;
2015-07-30 15:22:07 +00:00
if ( tx = = WALLET_LEGACY_INVALID_TRANSACTION_ID ) {
2015-05-27 12:08:46 +00:00
throw std : : runtime_error ( " Couldn't send transaction " ) ;
}
std : : error_code sendError = sent . wait ( tx ) ;
removeGuard . removeObserver ( ) ;
2015-04-06 16:13:07 +00:00
if ( sendError ) {
2015-05-27 12:08:46 +00:00
throw std : : system_error ( sendError ) ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
2015-07-30 15:22:07 +00:00
CryptoNote : : WalletLegacyTransaction txInfo ;
2015-04-06 16:13:07 +00:00
m_wallet . getTransaction ( tx , txInfo ) ;
2015-05-27 12:08:46 +00:00
res . tx_hash = Common : : podToHex ( txInfo . hash ) ;
2015-04-06 16:13:07 +00:00
} catch ( const std : : exception & e ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR , e . what ( ) ) ;
2014-04-02 16:00:17 +00:00
}
2015-04-06 16:13:07 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_store ( const wallet_rpc : : COMMAND_RPC_STORE : : request & req , wallet_rpc : : COMMAND_RPC_STORE : : response & res ) {
2015-04-06 16:13:07 +00:00
try {
2015-05-27 12:08:46 +00:00
WalletHelper : : storeWallet ( m_wallet , m_walletFilename ) ;
2015-04-06 16:13:07 +00:00
} catch ( std : : exception & e ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR , std : : string ( " Couldn't save wallet: " ) + e . what ( ) ) ;
2015-04-06 16:13:07 +00:00
}
2015-05-27 12:08:46 +00:00
2015-04-06 16:13:07 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_get_payments ( const wallet_rpc : : COMMAND_RPC_GET_PAYMENTS : : request & req , wallet_rpc : : COMMAND_RPC_GET_PAYMENTS : : response & res ) {
2015-07-30 15:22:07 +00:00
Crypto : : Hash expectedPaymentId ;
CryptoNote : : BinaryArray payment_id_blob ;
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
if ( ! Common : : fromHex ( req . payment_id , payment_id_blob ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID , " Payment ID has invald format " ) ;
2014-04-02 16:00:17 +00:00
}
2014-04-29 16:26:45 +00:00
2015-04-06 16:13:07 +00:00
if ( sizeof ( expectedPaymentId ) ! = payment_id_blob . size ( ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError ( WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID , " Payment ID has invalid size " ) ;
2015-04-06 16:13:07 +00:00
}
2015-07-30 15:22:07 +00:00
expectedPaymentId = * reinterpret_cast < const Crypto : : Hash * > ( payment_id_blob . data ( ) ) ;
2015-04-06 16:13:07 +00:00
size_t transactionsCount = m_wallet . getTransactionCount ( ) ;
for ( size_t trantransactionNumber = 0 ; trantransactionNumber < transactionsCount ; + + trantransactionNumber ) {
2015-07-30 15:22:07 +00:00
WalletLegacyTransaction txInfo ;
2015-04-06 16:13:07 +00:00
m_wallet . getTransaction ( trantransactionNumber , txInfo ) ;
2015-07-30 15:22:07 +00:00
if ( txInfo . state ! = WalletLegacyTransactionState : : Active | | txInfo . blockHeight = = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT ) {
2015-04-06 16:13:07 +00:00
continue ;
2014-04-29 16:26:45 +00:00
}
2015-04-06 16:13:07 +00:00
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 ) ; } ) ;
2014-04-29 16:26:45 +00:00
2015-07-30 15:22:07 +00:00
Crypto : : Hash paymentId ;
2015-04-06 16:13:07 +00:00
if ( getPaymentIdFromTxExtra ( extraVec , paymentId ) & & paymentId = = expectedPaymentId ) {
2014-04-29 16:26:45 +00:00
wallet_rpc : : payment_details rpc_payment ;
2015-05-27 12:08:46 +00:00
rpc_payment . tx_hash = Common : : podToHex ( txInfo . hash ) ;
2015-04-06 16:13:07 +00:00
rpc_payment . amount = txInfo . totalAmount ;
2015-05-27 12:08:46 +00:00
rpc_payment . block_height = txInfo . blockHeight ;
2015-04-06 16:13:07 +00:00
rpc_payment . unlock_time = txInfo . unlockTime ;
2014-04-29 16:26:45 +00:00
res . payments . push_back ( rpc_payment ) ;
}
}
2014-08-25 14:35:07 +00:00
2015-04-06 16:13:07 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_get_transfers ( const wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : request & req , wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : response & res ) {
2015-04-06 16:13:07 +00:00
res . transfers . clear ( ) ;
size_t transactionsCount = m_wallet . getTransactionCount ( ) ;
for ( size_t trantransactionNumber = 0 ; trantransactionNumber < transactionsCount ; + + trantransactionNumber ) {
2015-07-30 15:22:07 +00:00
WalletLegacyTransaction txInfo ;
2015-04-06 16:13:07 +00:00
m_wallet . getTransaction ( trantransactionNumber , txInfo ) ;
2015-07-30 15:22:07 +00:00
if ( txInfo . state ! = WalletLegacyTransactionState : : Active | | txInfo . blockHeight = = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT ) {
2015-04-06 16:13:07 +00:00
continue ;
2014-08-25 14:35:07 +00:00
}
2015-04-06 16:13:07 +00:00
std : : string address = " " ;
if ( txInfo . totalAmount < 0 ) {
if ( txInfo . transferCount > 0 ) {
2015-07-30 15:22:07 +00:00
WalletLegacyTransfer tr ;
2015-04-06 16:13:07 +00:00
m_wallet . getTransfer ( txInfo . firstTransferId , tr ) ;
address = tr . address ;
}
}
2014-08-25 14:35:07 +00:00
2015-04-06 16:13:07 +00:00
wallet_rpc : : Transfer transfer ;
transfer . time = txInfo . timestamp ;
transfer . output = txInfo . totalAmount < 0 ;
2015-05-27 12:08:46 +00:00
transfer . transactionHash = Common : : podToHex ( txInfo . hash ) ;
transfer . amount = std : : abs ( txInfo . totalAmount ) ;
2015-04-06 16:13:07 +00:00
transfer . fee = txInfo . fee ;
transfer . address = address ;
transfer . blockIndex = txInfo . blockHeight ;
transfer . unlockTime = txInfo . unlockTime ;
transfer . paymentId = " " ;
2014-08-25 14:35:07 +00:00
2015-04-06 16:13:07 +00:00
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 ) ; } ) ;
2015-07-30 15:22:07 +00:00
Crypto : : Hash paymentId ;
transfer . paymentId = ( getPaymentIdFromTxExtra ( extraVec , paymentId ) & & paymentId ! = NULL_HASH ? Common : : podToHex ( paymentId ) : " " ) ;
2015-04-06 16:13:07 +00:00
res . transfers . push_back ( transfer ) ;
2014-08-25 14:35:07 +00:00
}
2015-04-06 16:13:07 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_get_height ( const wallet_rpc : : COMMAND_RPC_GET_HEIGHT : : request & req , wallet_rpc : : COMMAND_RPC_GET_HEIGHT : : response & res ) {
2015-04-06 16:13:07 +00:00
res . height = m_node . getLastLocalBlockHeight ( ) ;
return true ;
}
2015-05-27 12:08:46 +00:00
bool wallet_rpc_server : : on_reset ( const wallet_rpc : : COMMAND_RPC_RESET : : request & req , wallet_rpc : : COMMAND_RPC_RESET : : response & res ) {
2015-04-06 16:13:07 +00:00
m_wallet . reset ( ) ;
return true ;
}
2014-04-29 16:26:45 +00:00
}