2015-01-02 16:52:46 +00:00
// Copyright (c) 2014-2015, The Monero Project
2014-07-23 13:03:52 +00:00
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
2014-04-02 16:00:17 +00:00
# include "include_base_utils.h"
using namespace epee ;
# include "wallet_rpc_server.h"
# include "common/command_line.h"
# include "cryptonote_core/cryptonote_format_utils.h"
# include "cryptonote_core/account.h"
2014-06-17 22:15:21 +00:00
# include "wallet_rpc_server_commands_defs.h"
2014-04-02 16:00:17 +00:00
# include "misc_language.h"
2014-05-03 16:19:43 +00:00
# include "string_tools.h"
2014-04-02 16:00:17 +00:00
# include "crypto/hash.h"
namespace tools
{
//-----------------------------------------------------------------------------------
const command_line : : arg_descriptor < std : : string > wallet_rpc_server : : arg_rpc_bind_port = { " rpc-bind-port " , " Starts wallet as rpc server for wallet operations, sets bind port for server " , " " , true } ;
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 " } ;
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 ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server : : wallet_rpc_server ( wallet2 & w ) : m_wallet ( w )
{ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : run ( )
{
m_net_server . add_idle_handler ( [ this ] ( ) {
2014-11-01 06:30:53 +00:00
try {
m_wallet . refresh ( ) ;
} catch ( const std : : exception & ex ) {
LOG_ERROR ( " Exception at while refreshing, what= " < < ex . what ( ) ) ;
}
2014-04-02 16:00:17 +00:00
return true ;
} , 20000 ) ;
//DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING
return epee : : http_server_impl_base < wallet_rpc_server , connection_context > : : run ( 1 , true ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
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 )
{
m_net_server . set_threads_prefix ( " RPC " ) ;
bool r = handle_command_line ( vm ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to process command line in core_rpc_server " ) ;
return epee : : http_server_impl_base < wallet_rpc_server , connection_context > : : init ( m_port , m_bind_ip ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_getbalance ( const wallet_rpc : : COMMAND_RPC_GET_BALANCE : : request & req , wallet_rpc : : COMMAND_RPC_GET_BALANCE : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
try
{
res . balance = m_wallet . balance ( ) ;
res . unlocked_balance = m_wallet . unlocked_balance ( ) ;
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-24 22:20:46 +00:00
bool wallet_rpc_server : : on_getaddress ( const wallet_rpc : : COMMAND_RPC_GET_ADDRESS : : request & req , wallet_rpc : : COMMAND_RPC_GET_ADDRESS : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
try
{
2014-09-09 14:58:53 +00:00
res . address = m_wallet . get_account ( ) . get_public_address_str ( m_wallet . testnet ( ) ) ;
2014-05-24 22:20:46 +00:00
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
2014-06-17 22:15:21 +00:00
2014-05-24 22:20:46 +00:00
//------------------------------------------------------------------------------------------------------------------------------
2014-07-23 15:27:23 +00:00
bool wallet_rpc_server : : validate_transfer ( const std : : list < wallet_rpc : : transfer_destination > destinations , const std : : string payment_id , std : : vector < cryptonote : : tx_destination_entry > & dsts , std : : vector < uint8_t > & extra , epee : : json_rpc : : error & er )
2014-04-02 16:00:17 +00:00
{
2014-06-17 22:15:21 +00:00
for ( auto it = destinations . begin ( ) ; it ! = destinations . end ( ) ; it + + )
2014-04-02 16:00:17 +00:00
{
cryptonote : : tx_destination_entry de ;
2014-09-09 14:58:53 +00:00
if ( ! get_account_address_from_str ( de . addr , m_wallet . testnet ( ) , it - > address ) )
2014-04-02 16:00:17 +00:00
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS ;
er . message = std : : string ( " WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: " ) + it - > address ;
return false ;
}
de . amount = it - > amount ;
dsts . push_back ( de ) ;
}
2014-06-01 22:22:42 +00:00
2014-06-17 22:15:21 +00:00
if ( ! payment_id . empty ( ) )
{
2014-06-01 22:22:42 +00:00
/* Just to clarify */
2014-06-17 22:15:21 +00:00
const std : : string & payment_id_str = payment_id ;
2014-06-01 22:22:42 +00:00
crypto : : hash payment_id ;
/* Parse payment ID */
if ( ! wallet2 : : parse_payment_id ( payment_id_str , payment_id ) ) {
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment id has invalid format: \" " + payment_id_str + " \" , expected 64-character string " ;
return false ;
}
std : : string extra_nonce ;
cryptonote : : set_payment_id_to_tx_extra_nonce ( extra_nonce , payment_id ) ;
/* Append Payment ID data into extra */
if ( ! cryptonote : : add_extra_nonce_to_tx_extra ( extra , extra_nonce ) ) {
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Something went wront with payment_id. Please check its format: \" " + payment_id_str + " \" , expected 64-character string " ;
return false ;
}
}
2014-06-17 22:15:21 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_transfer ( const wallet_rpc : : COMMAND_RPC_TRANSFER : : request & req , wallet_rpc : : COMMAND_RPC_TRANSFER : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < uint8_t > extra ;
2015-01-11 11:06:35 +00:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-06-17 22:15:21 +00:00
// validate the transfer requested and populate dsts & extra
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
{
return false ;
}
try
{
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions ( dsts , req . mixin , req . unlock_time , req . fee , extra ) ;
// reject proposed transactions if there are more than one. see on_transfer_split below.
if ( ptx_vector . size ( ) ! = 1 )
{
er . code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR ;
er . message = " Transaction would be too large. try /transfer_split. " ;
return false ;
}
m_wallet . commit_tx ( ptx_vector ) ;
// populate response with tx hash
res . tx_hash = boost : : lexical_cast < std : : string > ( cryptonote : : get_transaction_hash ( ptx_vector . back ( ) . tx ) ) ;
return true ;
}
catch ( const tools : : error : : daemon_busy & e )
{
er . code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY ;
er . message = e . what ( ) ;
return false ;
}
catch ( const std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR ;
er . message = e . what ( ) ;
return false ;
}
catch ( . . . )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = " WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR " ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_transfer_split ( const wallet_rpc : : COMMAND_RPC_TRANSFER_SPLIT : : request & req , wallet_rpc : : COMMAND_RPC_TRANSFER_SPLIT : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < uint8_t > extra ;
2015-01-11 11:06:35 +00:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-06-17 22:15:21 +00:00
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
{
return false ;
}
2014-06-01 22:22:42 +00:00
2014-04-02 16:00:17 +00:00
try
{
2014-06-17 22:15:21 +00:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions ( dsts , req . mixin , req . unlock_time , req . fee , extra ) ;
m_wallet . commit_tx ( ptx_vector ) ;
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
res . tx_hash_list . push_back ( boost : : lexical_cast < std : : string > ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
}
2014-04-02 16:00:17 +00:00
return true ;
}
catch ( const tools : : error : : daemon_busy & e )
{
er . code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY ;
er . message = e . what ( ) ;
2014-05-03 16:19:43 +00:00
return false ;
2014-04-02 16:00:17 +00:00
}
catch ( const std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR ;
er . message = e . what ( ) ;
2014-05-03 16:19:43 +00:00
return false ;
2014-04-02 16:00:17 +00:00
}
catch ( . . . )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = " WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR " ;
2014-05-03 16:19:43 +00:00
return false ;
2014-04-02 16:00:17 +00:00
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_store ( const wallet_rpc : : COMMAND_RPC_STORE : : request & req , wallet_rpc : : COMMAND_RPC_STORE : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
2015-01-11 11:06:35 +00:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-04-02 16:00:17 +00:00
try
{
m_wallet . store ( ) ;
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-03 16:19:43 +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 , epee : : json_rpc : : error & er , connection_context & cntx )
{
crypto : : hash payment_id ;
cryptonote : : blobdata payment_id_blob ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( req . payment_id , payment_id_blob ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invald format " ;
return false ;
}
if ( sizeof ( payment_id ) ! = payment_id_blob . size ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invalid size " ;
return false ;
}
payment_id = * reinterpret_cast < const crypto : : hash * > ( payment_id_blob . data ( ) ) ;
res . payments . clear ( ) ;
std : : list < wallet2 : : payment_details > payment_list ;
m_wallet . get_payments ( payment_id , payment_list ) ;
2014-07-22 16:00:25 +00:00
for ( auto & payment : payment_list )
2014-05-03 16:19:43 +00:00
{
wallet_rpc : : payment_details rpc_payment ;
2014-07-22 16:00:25 +00:00
rpc_payment . payment_id = req . payment_id ;
2014-05-03 16:19:43 +00:00
rpc_payment . tx_hash = epee : : string_tools : : pod_to_hex ( payment . m_tx_hash ) ;
rpc_payment . amount = payment . m_amount ;
rpc_payment . block_height = payment . m_block_height ;
rpc_payment . unlock_time = payment . m_unlock_time ;
res . payments . push_back ( rpc_payment ) ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-07-22 16:00:25 +00:00
bool wallet_rpc_server : : on_get_bulk_payments ( const wallet_rpc : : COMMAND_RPC_GET_BULK_PAYMENTS : : request & req , wallet_rpc : : COMMAND_RPC_GET_BULK_PAYMENTS : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
res . payments . clear ( ) ;
2015-01-10 19:32:08 +00:00
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
if ( req . payment_ids . empty ( ) )
{
std : : list < std : : pair < crypto : : hash , wallet2 : : payment_details > > payment_list ;
m_wallet . get_payments ( payment_list , req . min_block_height ) ;
for ( auto & payment : payment_list )
{
wallet_rpc : : payment_details rpc_payment ;
rpc_payment . payment_id = epee : : string_tools : : pod_to_hex ( payment . first ) ;
rpc_payment . tx_hash = epee : : string_tools : : pod_to_hex ( payment . second . m_tx_hash ) ;
rpc_payment . amount = payment . second . m_amount ;
rpc_payment . block_height = payment . second . m_block_height ;
rpc_payment . unlock_time = payment . second . m_unlock_time ;
res . payments . push_back ( std : : move ( rpc_payment ) ) ;
}
return true ;
}
2014-07-22 16:00:25 +00:00
for ( auto & payment_id_str : req . payment_ids )
{
crypto : : hash payment_id ;
cryptonote : : blobdata payment_id_blob ;
// TODO - should the whole thing fail because of one bad id?
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( payment_id_str , payment_id_blob ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invalid format: " + payment_id_str ;
return false ;
}
if ( sizeof ( payment_id ) ! = payment_id_blob . size ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invalid size: " + payment_id_str ;
return false ;
}
payment_id = * reinterpret_cast < const crypto : : hash * > ( payment_id_blob . data ( ) ) ;
std : : list < wallet2 : : payment_details > payment_list ;
m_wallet . get_payments ( payment_id , payment_list , req . min_block_height ) ;
for ( auto & payment : payment_list )
{
wallet_rpc : : payment_details rpc_payment ;
rpc_payment . payment_id = payment_id_str ;
rpc_payment . tx_hash = epee : : string_tools : : pod_to_hex ( payment . m_tx_hash ) ;
rpc_payment . amount = payment . m_amount ;
rpc_payment . block_height = payment . m_block_height ;
rpc_payment . unlock_time = payment . m_unlock_time ;
res . payments . push_back ( std : : move ( rpc_payment ) ) ;
}
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-27 10:52:11 +00:00
bool wallet_rpc_server : : on_incoming_transfers ( const wallet_rpc : : COMMAND_RPC_INCOMING_TRANSFERS : : request & req , wallet_rpc : : COMMAND_RPC_INCOMING_TRANSFERS : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
if ( req . transfer_type . compare ( " all " ) ! = 0 & & req . transfer_type . compare ( " available " ) ! = 0 & & req . transfer_type . compare ( " unavailable " ) ! = 0 )
{
er . code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE ;
2014-06-02 20:36:35 +00:00
er . message = " Transfer type must be one of: all, available, or unavailable " ;
2014-05-27 10:52:11 +00:00
return false ;
}
bool filter = false ;
bool available = false ;
if ( req . transfer_type . compare ( " available " ) = = 0 )
{
filter = true ;
available = true ;
}
else if ( req . transfer_type . compare ( " unavailable " ) = = 0 )
{
filter = true ;
available = false ;
}
wallet2 : : transfer_container transfers ;
m_wallet . get_transfers ( transfers ) ;
bool transfers_found = false ;
for ( const auto & td : transfers )
{
if ( ! filter | | available ! = td . m_spent )
{
if ( ! transfers_found )
{
transfers_found = true ;
}
wallet_rpc : : transfer_details rpc_transfers ;
rpc_transfers . amount = td . amount ( ) ;
rpc_transfers . spent = td . m_spent ;
rpc_transfers . global_index = td . m_global_output_index ;
rpc_transfers . tx_hash = boost : : lexical_cast < std : : string > ( cryptonote : : get_transaction_hash ( td . m_tx ) ) ;
res . transfers . push_back ( rpc_transfers ) ;
}
}
if ( ! transfers_found )
{
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-08-05 06:17:23 +00:00
bool wallet_rpc_server : : on_query_key ( const wallet_rpc : : COMMAND_RPC_QUERY_KEY : : request & req , wallet_rpc : : COMMAND_RPC_QUERY_KEY : : response & res , epee : : json_rpc : : error & er , connection_context & cntx )
{
2015-01-11 11:06:35 +00:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-08-05 06:17:23 +00:00
if ( req . key_type . compare ( " mnemonic " ) = = 0 )
{
if ( ! m_wallet . get_seed ( res . key ) )
{
er . message = " The wallet is non-deterministic. Cannot display seed. " ;
return false ;
}
}
2014-08-05 06:57:08 +00:00
else if ( req . key_type . compare ( " view_key " ) = = 0 )
{
res . key = string_tools : : pod_to_hex ( m_wallet . get_account ( ) . get_keys ( ) . m_view_secret_key ) ;
}
2014-08-05 06:17:23 +00:00
else
{
er . message = " key_type " + req . key_type + " not found " ;
return false ;
}
return true ;
}
2014-05-03 16:19:43 +00:00
}