2015-12-31 06:39:56 +00:00
// Copyright (c) 2014-2016, 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
2016-11-28 23:39:59 +00:00
# include <boost/asio/ip/address.hpp>
2016-12-16 23:08:24 +00:00
# include <boost/filesystem/operations.hpp>
2016-11-09 03:55:41 +00:00
# include <cstdint>
2014-04-02 16:00:17 +00:00
# include "include_base_utils.h"
using namespace epee ;
# include "wallet_rpc_server.h"
2016-11-09 03:55:41 +00:00
# include "wallet/wallet_args.h"
2014-04-02 16:00:17 +00:00
# include "common/command_line.h"
2016-11-09 03:55:41 +00:00
# include "common/i18n.h"
2016-12-16 23:08:24 +00:00
# include "common/util.h"
2014-04-02 16:00:17 +00:00
# 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"
2016-12-16 23:08:24 +00:00
# include "string_coding.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"
2016-11-09 03:55:41 +00:00
namespace
2014-04-02 16:00:17 +00:00
{
2016-11-09 03:55:41 +00:00
const command_line : : arg_descriptor < std : : string , true > arg_rpc_bind_port = { " rpc-bind-port " , " Sets bind port for server " } ;
const command_line : : arg_descriptor < std : : string > arg_rpc_bind_ip = { " rpc-bind-ip " , " Specify ip to bind rpc server " , " 127.0.0.1 " } ;
2016-12-16 23:08:24 +00:00
const command_line : : arg_descriptor < std : : string > arg_rpc_login = { " rpc-login " , " Specify username[:password] required for RPC connection " } ;
const command_line : : arg_descriptor < bool > arg_disable_rpc_login = { " disable-rpc-login " , " Disable HTTP authentication for RPC " } ;
2016-11-28 23:39:59 +00:00
const command_line : : arg_descriptor < bool > arg_confirm_external_bind = { " confirm-external-bind " , " Confirm rcp-bind-ip value is NOT a loopback (local) IP " } ;
2016-12-16 23:08:24 +00:00
constexpr const char default_rpc_username [ ] = " monero " ;
2016-11-09 03:55:41 +00:00
}
2014-04-02 16:00:17 +00:00
2016-11-09 03:55:41 +00:00
namespace tools
{
const char * wallet_rpc_server : : tr ( const char * str )
2014-04-02 16:00:17 +00:00
{
2016-11-09 03:55:41 +00:00
return i18n_translate ( str , " tools::wallet_rpc_server " ) ;
2014-04-02 16:00:17 +00:00
}
2016-11-09 03:55:41 +00:00
2014-04-02 16:00:17 +00:00
//------------------------------------------------------------------------------------------------------------------------------
2016-12-16 23:08:24 +00:00
wallet_rpc_server : : wallet_rpc_server ( wallet2 & w ) : m_wallet ( w ) , rpc_login_filename ( ) , m_stop ( false )
2014-04-02 16:00:17 +00:00
{ }
//------------------------------------------------------------------------------------------------------------------------------
2016-12-16 23:08:24 +00:00
wallet_rpc_server : : ~ wallet_rpc_server ( )
{
try
{
boost : : system : : error_code ec { } ;
boost : : filesystem : : remove ( rpc_login_filename , ec ) ;
}
catch ( . . . ) { }
}
//------------------------------------------------------------------------------------------------------------------------------
2014-04-02 16:00:17 +00:00
bool wallet_rpc_server : : run ( )
{
2015-12-28 23:38:30 +00:00
m_stop = false ;
2014-04-02 16:00:17 +00:00
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 ) ;
2015-12-28 23:38:30 +00:00
m_net_server . add_idle_handler ( [ this ] ( ) {
if ( m_stop . load ( std : : memory_order_relaxed ) )
{
send_stop_signal ( ) ;
return false ;
}
return true ;
} , 500 ) ;
2014-04-02 16:00:17 +00:00
//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 : : init ( const boost : : program_options : : variables_map & vm )
{
2016-11-28 23:39:59 +00:00
std : : string bind_ip = command_line : : get_arg ( vm , arg_rpc_bind_ip ) ;
if ( ! bind_ip . empty ( ) )
{
// always parse IP here for error consistency
boost : : system : : error_code ec { } ;
const auto parsed_ip = boost : : asio : : ip : : address : : from_string ( bind_ip , ec ) ;
if ( ec )
{
LOG_ERROR ( tr ( " Invalid IP address given for rpc-bind-ip argument " ) ) ;
return false ;
}
if ( ! parsed_ip . is_loopback ( ) & & ! command_line : : get_arg ( vm , arg_confirm_external_bind ) )
{
LOG_ERROR (
tr ( " The rpc-bind-ip value is listening for unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --confirm-external-bind " )
) ;
return false ;
}
}
2016-12-30 06:47:57 +00:00
epee : : net_utils : : http : : login login { } ;
2016-12-16 23:08:24 +00:00
const bool disable_auth = command_line : : get_arg ( vm , arg_disable_rpc_login ) ;
const std : : string user_pass = command_line : : get_arg ( vm , arg_rpc_login ) ;
const std : : string bind_port = command_line : : get_arg ( vm , arg_rpc_bind_port ) ;
if ( disable_auth )
{
if ( ! user_pass . empty ( ) )
{
LOG_ERROR ( tr ( " Cannot specify -- " ) < < arg_disable_rpc_login . name < < tr ( " and -- " ) < < arg_rpc_login . name ) ;
return false ;
}
}
else // auth enabled
{
if ( user_pass . empty ( ) )
{
login . username = default_rpc_username ;
std : : array < std : : uint8_t , 16 > rand_128bit { { } } ;
crypto : : rand ( rand_128bit . size ( ) , rand_128bit . data ( ) ) ;
login . password = string_encoding : : base64_encode ( rand_128bit . data ( ) , rand_128bit . size ( ) ) ;
}
else // user password
{
const auto loc = user_pass . find ( ' : ' ) ;
login . username = user_pass . substr ( 0 , loc ) ;
if ( loc ! = std : : string : : npos )
{
login . password = user_pass . substr ( loc + 1 ) ;
}
else
{
2016-12-17 23:07:15 +00:00
login . password = tools : : password_container : : prompt ( true , " RPC password " ) . value_or (
tools : : password_container { }
) . password ( ) ;
2016-12-16 23:08:24 +00:00
}
if ( login . username . empty ( ) | | login . password . empty ( ) )
{
LOG_ERROR ( tr ( " Blank username or password not permitted for RPC authenticaion " ) ) ;
return false ;
}
}
assert ( ! login . username . empty ( ) ) ;
assert ( ! login . password . empty ( ) ) ;
std : : string temp = " monero-wallet-rpc. " + bind_port + " .login " ;
const auto cookie = tools : : create_private_file ( temp ) ;
if ( ! cookie )
{
LOG_ERROR ( tr ( " Failed to create file " ) < < temp < < tr ( " . Check permissions or remove file " ) ) ;
return false ;
}
rpc_login_filename . swap ( temp ) ; // nothrow guarantee destructor cleanup
temp = rpc_login_filename ;
std : : fputs ( login . username . c_str ( ) , cookie . get ( ) ) ;
std : : fputc ( ' : ' , cookie . get ( ) ) ;
std : : fputs ( login . password . c_str ( ) , cookie . get ( ) ) ;
std : : fflush ( cookie . get ( ) ) ;
if ( std : : ferror ( cookie . get ( ) ) )
{
LOG_ERROR ( tr ( " Error writing to file " ) < < temp ) ;
return false ;
}
LOG_PRINT_L0 ( tr ( " RPC username/password is stored in file " ) < < temp ) ;
} // end auth enabled
2014-04-02 16:00:17 +00:00
m_net_server . set_threads_prefix ( " RPC " ) ;
2016-11-28 23:39:59 +00:00
return epee : : http_server_impl_base < wallet_rpc_server , connection_context > : : init (
2016-12-16 23:08:24 +00:00
std : : move ( bind_port ) , std : : move ( bind_ip ) , std : : string { } , boost : : make_optional ( ! disable_auth , std : : move ( login ) )
2016-11-28 23:39:59 +00:00
) ;
2014-04-02 16:00:17 +00:00
}
//------------------------------------------------------------------------------------------------------------------------------
2017-01-08 13:16:22 +00:00
void wallet_rpc_server : : fill_transfer_entry ( tools : : wallet_rpc : : transfer_entry & entry , const crypto : : hash & txid , const crypto : : hash & payment_id , const tools : : wallet2 : : payment_details & pd )
{
entry . txid = string_tools : : pod_to_hex ( pd . m_tx_hash ) ;
entry . payment_id = string_tools : : pod_to_hex ( payment_id ) ;
if ( entry . payment_id . substr ( 16 ) . find_first_not_of ( ' 0 ' ) = = std : : string : : npos )
entry . payment_id = entry . payment_id . substr ( 0 , 16 ) ;
entry . height = pd . m_block_height ;
entry . timestamp = pd . m_timestamp ;
entry . amount = pd . m_amount ;
entry . fee = 0 ; // TODO
entry . note = m_wallet . get_tx_note ( pd . m_tx_hash ) ;
entry . type = " in " ;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server : : fill_transfer_entry ( tools : : wallet_rpc : : transfer_entry & entry , const crypto : : hash & txid , const tools : : wallet2 : : confirmed_transfer_details & pd )
{
entry . txid = string_tools : : pod_to_hex ( txid ) ;
entry . payment_id = string_tools : : pod_to_hex ( pd . m_payment_id ) ;
if ( entry . payment_id . substr ( 16 ) . find_first_not_of ( ' 0 ' ) = = std : : string : : npos )
entry . payment_id = entry . payment_id . substr ( 0 , 16 ) ;
entry . height = pd . m_block_height ;
entry . timestamp = pd . m_timestamp ;
entry . fee = pd . m_amount_in - pd . m_amount_out ;
uint64_t change = pd . m_change = = ( uint64_t ) - 1 ? 0 : pd . m_change ; // change may not be known
entry . amount = pd . m_amount_in - change - entry . fee ;
entry . note = m_wallet . get_tx_note ( txid ) ;
for ( const auto & d : pd . m_dests ) {
entry . destinations . push_back ( wallet_rpc : : transfer_destination ( ) ) ;
wallet_rpc : : transfer_destination & td = entry . destinations . back ( ) ;
td . amount = d . amount ;
td . address = get_account_address_as_str ( m_wallet . testnet ( ) , d . addr ) ;
}
entry . type = " out " ;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server : : fill_transfer_entry ( tools : : wallet_rpc : : transfer_entry & entry , const crypto : : hash & txid , const tools : : wallet2 : : unconfirmed_transfer_details & pd )
{
bool is_failed = pd . m_state = = tools : : wallet2 : : unconfirmed_transfer_details : : failed ;
entry . txid = string_tools : : pod_to_hex ( txid ) ;
entry . payment_id = string_tools : : pod_to_hex ( pd . m_payment_id ) ;
entry . payment_id = string_tools : : pod_to_hex ( pd . m_payment_id ) ;
if ( entry . payment_id . substr ( 16 ) . find_first_not_of ( ' 0 ' ) = = std : : string : : npos )
entry . payment_id = entry . payment_id . substr ( 0 , 16 ) ;
entry . height = 0 ;
entry . timestamp = pd . m_timestamp ;
entry . fee = pd . m_amount_in - pd . m_amount_out ;
entry . amount = pd . m_amount_in - pd . m_change - entry . fee ;
entry . note = m_wallet . get_tx_note ( txid ) ;
entry . type = is_failed ? " failed " : " pending " ;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server : : fill_transfer_entry ( tools : : wallet_rpc : : transfer_entry & entry , const crypto : : hash & payment_id , const tools : : wallet2 : : payment_details & pd )
{
entry . txid = string_tools : : pod_to_hex ( pd . m_tx_hash ) ;
entry . payment_id = string_tools : : pod_to_hex ( payment_id ) ;
if ( entry . payment_id . substr ( 16 ) . find_first_not_of ( ' 0 ' ) = = std : : string : : npos )
entry . payment_id = entry . payment_id . substr ( 0 , 16 ) ;
entry . height = 0 ;
entry . timestamp = pd . m_timestamp ;
entry . amount = pd . m_amount ;
entry . fee = 0 ; // TODO
entry . note = m_wallet . get_tx_note ( pd . m_tx_hash ) ;
entry . type = " pool " ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +00:00
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 )
2014-04-02 16:00:17 +00:00
{
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +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 )
2014-05-24 22:20:46 +00:00
{
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 ;
}
2015-12-23 16:04:04 +00:00
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_getheight ( const wallet_rpc : : COMMAND_RPC_GET_HEIGHT : : request & req , wallet_rpc : : COMMAND_RPC_GET_HEIGHT : : response & res , epee : : json_rpc : : error & er )
{
try
{
res . height = m_wallet . get_blockchain_current_height ( ) ;
}
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
//------------------------------------------------------------------------------------------------------------------------------
2015-08-09 09:09:39 +00:00
bool wallet_rpc_server : : validate_transfer ( const std : : list < wallet_rpc : : transfer_destination > destinations , 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
{
2015-08-09 09:09:39 +00:00
crypto : : hash8 integrated_payment_id = cryptonote : : null_hash8 ;
std : : string extra_nonce ;
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 ;
2015-06-11 08:44:13 +00:00
bool has_payment_id ;
2015-08-09 09:09:39 +00:00
crypto : : hash8 new_payment_id ;
2015-06-11 08:44:13 +00:00
if ( ! get_account_integrated_address_from_str ( de . addr , has_payment_id , new_payment_id , 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 ) ;
2015-06-11 08:44:13 +00:00
if ( has_payment_id )
{
2015-08-09 09:09:39 +00:00
if ( ! payment_id . empty ( ) | | integrated_payment_id ! = cryptonote : : null_hash8 )
2015-06-11 08:44:13 +00:00
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " A single payment id is allowed per transaction " ;
return false ;
}
integrated_payment_id = new_payment_id ;
2015-08-09 09:09:39 +00:00
cryptonote : : set_encrypted_payment_id_to_tx_extra_nonce ( extra_nonce , integrated_payment_id ) ;
2016-07-10 12:45:01 +00:00
/* 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 wrong with integrated payment_id. " ;
return false ;
}
2015-06-11 08:44:13 +00:00
}
2014-04-02 16:00:17 +00:00
}
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
2015-08-09 09:09:39 +00:00
crypto : : hash long_payment_id ;
crypto : : hash8 short_payment_id ;
2014-06-01 22:22:42 +00:00
/* Parse payment ID */
2015-08-09 09:09:39 +00:00
if ( wallet2 : : parse_long_payment_id ( payment_id_str , long_payment_id ) ) {
cryptonote : : set_payment_id_to_tx_extra_nonce ( extra_nonce , long_payment_id ) ;
}
/* or short payment ID */
else if ( ! wallet2 : : parse_short_payment_id ( payment_id_str , short_payment_id ) ) {
cryptonote : : set_encrypted_payment_id_to_tx_extra_nonce ( extra_nonce , short_payment_id ) ;
}
else {
2014-06-01 22:22:42 +00:00
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
2015-08-09 09:09:39 +00:00
er . message = " Payment id has invalid format: \" " + payment_id_str + " \" , expected 16 or 64 character string " ;
2014-06-01 22:22:42 +00:00
return false ;
}
/* 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 ;
2016-07-10 12:45:01 +00:00
er . message = " Something went wrong with payment_id. Please check its format: \" " + payment_id_str + " \" , expected 64-character string " ;
2014-06-01 22:22:42 +00:00
return false ;
}
}
2014-06-17 22:15:21 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +00:00
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 )
2014-06-17 22:15:21 +00:00
{
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
2015-08-09 09:09:39 +00:00
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
2014-06-17 22:15:21 +00:00
{
return false ;
}
try
{
2016-03-11 21:32:16 +00:00
uint64_t mixin = req . mixin ;
2016-07-27 20:18:08 +00:00
if ( mixin < 2 & & m_wallet . use_fork_rules ( 2 , 10 ) ) {
2016-03-11 21:32:16 +00:00
LOG_PRINT_L1 ( " Requested mixin " < < req . mixin < < " too low for hard fork 2, using 2 " ) ;
mixin = 2 ;
}
2016-09-16 10:50:52 +00:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions_2 ( dsts , mixin , req . unlock_time , req . priority , extra , req . trusted_daemon ) ;
2014-06-17 22:15:21 +00:00
// 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
2016-06-09 20:16:00 +00:00
res . tx_hash = epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx_vector . back ( ) . tx ) ) ;
2015-08-19 19:59:44 +00:00
if ( req . get_tx_key )
2016-07-11 22:14:58 +00:00
{
2016-06-09 20:16:00 +00:00
res . tx_key = epee : : string_tools : : pod_to_hex ( ptx_vector . back ( ) . tx_key ) ;
2016-07-11 22:14:58 +00:00
}
2016-11-16 18:56:45 +00:00
res . fee = ptx_vector . back ( ) . fee ;
2014-06-17 22:15:21 +00:00
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +00:00
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 )
2014-06-17 22:15:21 +00:00
{
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.
2015-08-09 09:09:39 +00:00
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
2014-06-17 22:15:21 +00:00
{
return false ;
}
2014-06-01 22:22:42 +00:00
2014-04-02 16:00:17 +00:00
try
{
2016-03-11 21:32:16 +00:00
uint64_t mixin = req . mixin ;
2016-07-27 20:18:08 +00:00
if ( mixin < 2 & & m_wallet . use_fork_rules ( 2 , 10 ) ) {
2016-03-11 21:32:16 +00:00
LOG_PRINT_L1 ( " Requested mixin " < < req . mixin < < " too low for hard fork 2, using 2 " ) ;
mixin = 2 ;
}
2015-07-19 22:47:13 +00:00
std : : vector < wallet2 : : pending_tx > ptx_vector ;
2016-09-16 10:50:52 +00:00
ptx_vector = m_wallet . create_transactions_2 ( dsts , mixin , req . unlock_time , req . priority , extra , req . trusted_daemon ) ;
2014-06-17 22:15:21 +00:00
m_wallet . commit_tx ( ptx_vector ) ;
2015-05-30 14:37:27 +00:00
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 20:16:00 +00:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2015-08-19 19:59:44 +00:00
if ( req . get_tx_keys )
2016-07-11 22:14:58 +00:00
{
2016-06-09 20:16:00 +00:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2016-07-11 22:14:58 +00:00
}
2016-11-16 18:56:45 +00:00
res . fee_list . push_back ( ptx . fee ) ;
2015-05-30 14:37:27 +00:00
}
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_sweep_dust ( const wallet_rpc : : COMMAND_RPC_SWEEP_DUST : : request & req , wallet_rpc : : COMMAND_RPC_SWEEP_DUST : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
try
{
2016-03-26 23:22:57 +00:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_unmixable_sweep_transactions ( req . trusted_daemon ) ;
2015-05-30 14:37:27 +00:00
m_wallet . commit_tx ( ptx_vector ) ;
2014-06-17 22:15:21 +00:00
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 20:16:00 +00:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2015-08-19 19:59:44 +00:00
if ( req . get_tx_keys )
2016-07-11 22:14:58 +00:00
{
2016-06-09 20:16:00 +00:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2016-07-11 22:14:58 +00:00
}
2016-11-16 18:56:45 +00:00
res . fee_list . push_back ( ptx . fee ) ;
2014-06-17 22:15:21 +00:00
}
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 ;
2016-04-19 20:20:27 +00:00
er . message = " WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR " ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_sweep_all ( const wallet_rpc : : COMMAND_RPC_SWEEP_ALL : : request & req , wallet_rpc : : COMMAND_RPC_SWEEP_ALL : : response & res , epee : : json_rpc : : error & er )
{
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < uint8_t > extra ;
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
// validate the transfer requested and populate dsts & extra
std : : list < wallet_rpc : : transfer_destination > destination ;
destination . push_back ( wallet_rpc : : transfer_destination ( ) ) ;
destination . back ( ) . amount = 0 ;
destination . back ( ) . address = req . address ;
if ( ! validate_transfer ( destination , req . payment_id , dsts , extra , er ) )
{
return false ;
}
try
{
2016-09-16 10:50:52 +00:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions_all ( dsts [ 0 ] . addr , req . mixin , req . unlock_time , req . priority , extra , req . trusted_daemon ) ;
2016-04-19 20:20:27 +00:00
m_wallet . commit_tx ( ptx_vector ) ;
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 20:16:00 +00:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2016-04-19 20:20:27 +00:00
if ( req . get_tx_keys )
2016-07-11 22:14:58 +00:00
{
2016-06-09 20:16:00 +00:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2016-07-11 22:14:58 +00:00
}
2016-11-16 18:56:45 +00:00
res . fee_list . push_back ( ptx . fee ) ;
2016-04-19 20:20:27 +00:00
}
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 ;
2014-04-02 16:00:17 +00:00
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-06-13 15:08:00 +00:00
bool wallet_rpc_server : : on_make_integrated_address ( const wallet_rpc : : COMMAND_RPC_MAKE_INTEGRATED_ADDRESS : : request & req , wallet_rpc : : COMMAND_RPC_MAKE_INTEGRATED_ADDRESS : : response & res , epee : : json_rpc : : error & er )
{
try
{
2015-08-09 09:09:39 +00:00
crypto : : hash8 payment_id ;
2015-06-13 15:08:00 +00:00
if ( req . payment_id . empty ( ) )
{
2015-08-26 07:28:58 +00:00
payment_id = crypto : : rand < crypto : : hash8 > ( ) ;
2015-06-13 15:08:00 +00:00
}
else
{
2015-08-09 09:09:39 +00:00
if ( ! tools : : wallet2 : : parse_short_payment_id ( req . payment_id , payment_id ) )
2015-06-13 15:08:00 +00:00
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Invalid payment ID " ;
return false ;
}
}
res . integrated_address = m_wallet . get_account ( ) . get_public_integrated_address_str ( payment_id , m_wallet . testnet ( ) ) ;
2016-08-29 11:18:22 +00:00
res . payment_id = epee : : string_tools : : pod_to_hex ( payment_id ) ;
2015-06-13 15:08:00 +00:00
return true ;
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_split_integrated_address ( const wallet_rpc : : COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS : : request & req , wallet_rpc : : COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS : : response & res , epee : : json_rpc : : error & er )
{
try
{
cryptonote : : account_public_address address ;
2015-08-09 09:09:39 +00:00
crypto : : hash8 payment_id ;
2015-06-13 15:08:00 +00:00
bool has_payment_id ;
if ( ! get_account_integrated_address_from_str ( address , has_payment_id , payment_id , m_wallet . testnet ( ) , req . integrated_address ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS ;
er . message = " Invalid address " ;
return false ;
}
if ( ! has_payment_id )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS ;
er . message = " Address is not an integrated address " ;
return false ;
}
res . standard_address = get_account_address_as_str ( m_wallet . testnet ( ) , address ) ;
2016-06-09 20:16:00 +00:00
res . payment_id = epee : : string_tools : : pod_to_hex ( payment_id ) ;
2015-06-13 15:08:00 +00:00
return true ;
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +00:00
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 )
2014-04-02 16:00:17 +00:00
{
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +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 )
2014-05-03 16:19:43 +00:00
{
crypto : : hash payment_id ;
2016-03-05 19:30:48 +00:00
crypto : : hash8 payment_id8 ;
2014-05-03 16:19:43 +00:00
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 ;
2016-12-04 13:13:54 +00:00
er . message = " Payment ID has invalid format " ;
2014-05-03 16:19:43 +00:00
return false ;
}
2016-03-05 19:30:48 +00:00
if ( sizeof ( payment_id ) = = payment_id_blob . size ( ) )
{
payment_id = * reinterpret_cast < const crypto : : hash * > ( payment_id_blob . data ( ) ) ;
}
else if ( sizeof ( payment_id8 ) = = payment_id_blob . size ( ) )
{
payment_id8 = * reinterpret_cast < const crypto : : hash8 * > ( payment_id_blob . data ( ) ) ;
memcpy ( payment_id . data , payment_id8 . data , 8 ) ;
memset ( payment_id . data + 8 , 0 , 24 ) ;
}
else
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invalid size: " + req . payment_id ;
return false ;
}
2014-05-03 16:19:43 +00:00
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +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 )
2014-07-22 16:00:25 +00:00
{
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 ;
2015-08-09 09:09:39 +00:00
crypto : : hash8 payment_id8 ;
2014-07-22 16:00:25 +00:00
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 ;
}
2015-08-09 09:09:39 +00:00
if ( sizeof ( payment_id ) = = payment_id_blob . size ( ) )
{
payment_id = * reinterpret_cast < const crypto : : hash * > ( payment_id_blob . data ( ) ) ;
}
else if ( sizeof ( payment_id8 ) = = payment_id_blob . size ( ) )
{
payment_id8 = * reinterpret_cast < const crypto : : hash8 * > ( payment_id_blob . data ( ) ) ;
memcpy ( payment_id . data , payment_id8 . data , 8 ) ;
memset ( payment_id . data + 8 , 0 , 24 ) ;
}
else
2014-07-22 16:00:25 +00:00
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
er . message = " Payment ID has invalid size: " + payment_id_str ;
return false ;
}
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 ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +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 )
2014-05-27 10:52:11 +00:00
{
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 ;
}
2015-02-19 22:57:26 +00:00
auto txBlob = t_serializable_object_to_blob ( td . m_tx ) ;
2014-05-27 10:52:11 +00:00
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 ;
2016-08-06 18:19:25 +00:00
rpc_transfers . tx_hash = epee : : string_tools : : pod_to_hex ( td . m_txid ) ;
2015-02-19 22:57:26 +00:00
rpc_transfers . tx_size = txBlob . size ( ) ;
2014-05-27 10:52:11 +00:00
res . transfers . push_back ( rpc_transfers ) ;
}
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 12:01:30 +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 )
2014-08-05 06:17:23 +00:00
{
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 ;
}
2015-12-30 12:58:15 +00:00
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_rescan_blockchain ( const wallet_rpc : : COMMAND_RPC_RESCAN_BLOCKCHAIN : : request & req , wallet_rpc : : COMMAND_RPC_RESCAN_BLOCKCHAIN : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
try
{
m_wallet . rescan_blockchain ( ) ;
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-04-23 20:46:48 +00:00
bool wallet_rpc_server : : on_sign ( const wallet_rpc : : COMMAND_RPC_SIGN : : request & req , wallet_rpc : : COMMAND_RPC_SIGN : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
res . signature = m_wallet . sign ( req . data ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_verify ( const wallet_rpc : : COMMAND_RPC_VERIFY : : request & req , wallet_rpc : : COMMAND_RPC_VERIFY : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
cryptonote : : account_public_address address ;
bool has_payment_id ;
crypto : : hash8 payment_id ;
if ( ! get_account_integrated_address_from_str ( address , has_payment_id , payment_id , m_wallet . testnet ( ) , req . address ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS ;
er . message = " " ;
return false ;
}
res . good = m_wallet . verify ( req . data , address , req . signature ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-12-05 14:53:37 +00:00
bool wallet_rpc_server : : on_stop_wallet ( const wallet_rpc : : COMMAND_RPC_STOP_WALLET : : request & req , wallet_rpc : : COMMAND_RPC_STOP_WALLET : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
try
{
m_wallet . store ( ) ;
2015-12-28 23:38:30 +00:00
m_stop . store ( true , std : : memory_order_relaxed ) ;
2015-12-05 14:53:37 +00:00
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-04-20 17:19:42 +00:00
bool wallet_rpc_server : : on_set_tx_notes ( const wallet_rpc : : COMMAND_RPC_SET_TX_NOTES : : request & req , wallet_rpc : : COMMAND_RPC_SET_TX_NOTES : : response & res , epee : : json_rpc : : error & er )
{
if ( req . txids . size ( ) ! = req . notes . size ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = " Different amount of txids and notes " ;
return false ;
}
std : : list < crypto : : hash > txids ;
std : : list < std : : string > : : const_iterator i = req . txids . begin ( ) ;
while ( i ! = req . txids . end ( ) )
{
cryptonote : : blobdata txid_blob ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( * i + + , txid_blob ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_TXID ;
er . message = " TX ID has invalid format " ;
return false ;
}
crypto : : hash txid = * reinterpret_cast < const crypto : : hash * > ( txid_blob . data ( ) ) ;
txids . push_back ( txid ) ;
}
std : : list < crypto : : hash > : : const_iterator il = txids . begin ( ) ;
std : : list < std : : string > : : const_iterator in = req . notes . begin ( ) ;
while ( il ! = txids . end ( ) )
{
m_wallet . set_tx_note ( * il + + , * in + + ) ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_get_tx_notes ( const wallet_rpc : : COMMAND_RPC_GET_TX_NOTES : : request & req , wallet_rpc : : COMMAND_RPC_GET_TX_NOTES : : response & res , epee : : json_rpc : : error & er )
{
res . notes . clear ( ) ;
std : : list < crypto : : hash > txids ;
std : : list < std : : string > : : const_iterator i = req . txids . begin ( ) ;
while ( i ! = req . txids . end ( ) )
{
cryptonote : : blobdata txid_blob ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( * i + + , txid_blob ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_TXID ;
er . message = " TX ID has invalid format " ;
return false ;
}
crypto : : hash txid = * reinterpret_cast < const crypto : : hash * > ( txid_blob . data ( ) ) ;
txids . push_back ( txid ) ;
}
std : : list < crypto : : hash > : : const_iterator il = txids . begin ( ) ;
while ( il ! = txids . end ( ) )
{
res . notes . push_back ( m_wallet . get_tx_note ( * il + + ) ) ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-04-26 21:39:08 +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 , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2016-04-27 22:43:39 +00:00
uint64_t min_height = 0 , max_height = ( uint64_t ) - 1 ;
if ( req . filter_by_height )
{
min_height = req . min_height ;
max_height = req . max_height ;
}
2016-04-26 21:39:08 +00:00
if ( req . in )
{
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > payments ;
2016-04-27 22:43:39 +00:00
m_wallet . get_payments ( payments , min_height , max_height ) ;
2016-04-26 21:39:08 +00:00
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
2017-01-08 13:16:22 +00:00
res . in . push_back ( wallet_rpc : : transfer_entry ( ) ) ;
fill_transfer_entry ( res . in . back ( ) , i - > second . m_tx_hash , i - > first , i - > second ) ;
2016-04-26 21:39:08 +00:00
}
}
if ( req . out )
{
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > payments ;
2016-04-27 22:43:39 +00:00
m_wallet . get_payments_out ( payments , min_height , max_height ) ;
2016-04-26 21:39:08 +00:00
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
2017-01-08 13:16:22 +00:00
res . out . push_back ( wallet_rpc : : transfer_entry ( ) ) ;
fill_transfer_entry ( res . out . back ( ) , i - > first , i - > second ) ;
2016-04-26 21:39:08 +00:00
}
}
if ( req . pending | | req . failed ) {
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : unconfirmed_transfer_details > > upayments ;
m_wallet . get_unconfirmed_payments_out ( upayments ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : unconfirmed_transfer_details > > : : const_iterator i = upayments . begin ( ) ; i ! = upayments . end ( ) ; + + i ) {
const tools : : wallet2 : : unconfirmed_transfer_details & pd = i - > second ;
bool is_failed = pd . m_state = = tools : : wallet2 : : unconfirmed_transfer_details : : failed ;
if ( ! ( ( req . failed & & is_failed ) | | ( ! is_failed & & req . pending ) ) )
continue ;
2017-01-08 13:16:22 +00:00
std : : list < wallet_rpc : : transfer_entry > & entries = is_failed ? res . failed : res . pending ;
entries . push_back ( wallet_rpc : : transfer_entry ( ) ) ;
fill_transfer_entry ( entries . back ( ) , i - > first , i - > second ) ;
2016-04-26 21:39:08 +00:00
}
}
2016-05-23 20:40:12 +00:00
if ( req . pool )
{
m_wallet . update_pool_state ( ) ;
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > payments ;
m_wallet . get_unconfirmed_payments ( payments ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
2017-01-08 13:16:22 +00:00
res . pool . push_back ( wallet_rpc : : transfer_entry ( ) ) ;
fill_transfer_entry ( res . pool . back ( ) , i - > first , i - > second ) ;
2016-05-23 20:40:12 +00:00
}
}
2016-04-26 21:39:08 +00:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2017-01-08 13:16:22 +00:00
bool wallet_rpc_server : : on_get_transfer_by_txid ( const wallet_rpc : : COMMAND_RPC_GET_TRANSFER_BY_TXID : : request & req , wallet_rpc : : COMMAND_RPC_GET_TRANSFER_BY_TXID : : response & res , epee : : json_rpc : : error & er )
{
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
crypto : : hash txid ;
cryptonote : : blobdata txid_blob ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( req . txid , txid_blob ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_TXID ;
er . message = " Transaction ID has invalid format " ;
return false ;
}
if ( sizeof ( txid ) = = txid_blob . size ( ) )
{
txid = * reinterpret_cast < const crypto : : hash * > ( txid_blob . data ( ) ) ;
}
else
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_TXID ;
er . message = " Transaction ID has invalid size: " + req . txid ;
return false ;
}
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > payments ;
m_wallet . get_payments ( payments , 0 ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
if ( i - > first = = txid )
{
fill_transfer_entry ( res . transfer , i - > second . m_tx_hash , i - > first , i - > second ) ;
return true ;
}
}
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > payments_out ;
m_wallet . get_payments_out ( payments_out , 0 ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > : : const_iterator i = payments_out . begin ( ) ; i ! = payments_out . end ( ) ; + + i ) {
if ( i - > first = = txid )
{
fill_transfer_entry ( res . transfer , i - > first , i - > second ) ;
return true ;
}
}
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : unconfirmed_transfer_details > > upayments ;
m_wallet . get_unconfirmed_payments_out ( upayments ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : unconfirmed_transfer_details > > : : const_iterator i = upayments . begin ( ) ; i ! = upayments . end ( ) ; + + i ) {
if ( i - > first = = txid )
{
fill_transfer_entry ( res . transfer , i - > first , i - > second ) ;
return true ;
}
}
m_wallet . update_pool_state ( ) ;
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > pool_payments ;
m_wallet . get_unconfirmed_payments ( pool_payments ) ;
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > : : const_iterator i = pool_payments . begin ( ) ; i ! = pool_payments . end ( ) ; + + i ) {
if ( i - > second . m_tx_hash = = txid )
{
fill_transfer_entry ( res . transfer , i - > first , i - > second ) ;
return true ;
}
}
er . code = WALLET_RPC_ERROR_CODE_WRONG_TXID ;
er . message = " Transaction not found. " ;
return false ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-07-15 11:11:55 +00:00
bool wallet_rpc_server : : on_export_key_images ( const wallet_rpc : : COMMAND_RPC_EXPORT_KEY_IMAGES : : request & req , wallet_rpc : : COMMAND_RPC_EXPORT_KEY_IMAGES : : response & res , epee : : json_rpc : : error & er )
{
try
{
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > ski = m_wallet . export_key_images ( ) ;
res . signed_key_images . resize ( ski . size ( ) ) ;
for ( size_t n = 0 ; n < ski . size ( ) ; + + n )
{
res . signed_key_images [ n ] . key_image = epee : : string_tools : : pod_to_hex ( ski [ n ] . first ) ;
res . signed_key_images [ n ] . signature = epee : : string_tools : : pod_to_hex ( ski [ n ] . second ) ;
}
}
catch ( . . . )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = " Failed " ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_import_key_images ( const wallet_rpc : : COMMAND_RPC_IMPORT_KEY_IMAGES : : request & req , wallet_rpc : : COMMAND_RPC_IMPORT_KEY_IMAGES : : response & res , epee : : json_rpc : : error & er )
{
try
{
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > ski ;
ski . resize ( req . signed_key_images . size ( ) ) ;
for ( size_t n = 0 ; n < ski . size ( ) ; + + n )
{
cryptonote : : blobdata bd ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( req . signed_key_images [ n ] . key_image , bd ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE ;
er . message = " failed to parse key image " ;
return false ;
}
ski [ n ] . first = * reinterpret_cast < const crypto : : key_image * > ( bd . data ( ) ) ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( req . signed_key_images [ n ] . signature , bd ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE ;
er . message = " failed to parse signature " ;
return false ;
}
ski [ n ] . second = * reinterpret_cast < const crypto : : signature * > ( bd . data ( ) ) ;
}
uint64_t spent = 0 , unspent = 0 ;
uint64_t height = m_wallet . import_key_images ( ski , spent , unspent ) ;
res . spent = spent ;
res . unspent = unspent ;
res . height = height ;
}
catch ( . . . )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = " Failed " ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-11-28 14:07:25 +00:00
bool wallet_rpc_server : : on_make_uri ( const wallet_rpc : : COMMAND_RPC_MAKE_URI : : request & req , wallet_rpc : : COMMAND_RPC_MAKE_URI : : response & res , epee : : json_rpc : : error & er )
{
std : : string error ;
std : : string uri = m_wallet . make_uri ( req . address , req . payment_id , req . amount , req . tx_description , req . recipient_name , error ) ;
if ( uri . empty ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_URI ;
er . message = std : : string ( " Cannot make URI from supplied parameters: " ) + error ;
return false ;
}
res . uri = uri ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server : : on_parse_uri ( const wallet_rpc : : COMMAND_RPC_PARSE_URI : : request & req , wallet_rpc : : COMMAND_RPC_PARSE_URI : : response & res , epee : : json_rpc : : error & er )
{
std : : string error ;
if ( ! m_wallet . parse_uri ( req . uri , res . uri . address , res . uri . payment_id , res . uri . amount , res . uri . tx_description , res . uri . recipient_name , res . unknown_parameters , error ) )
{
er . code = WALLET_RPC_ERROR_CODE_WRONG_URI ;
er . message = " Error parsing URI: " + error ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-03 16:19:43 +00:00
}
2015-12-23 16:04:04 +00:00
2016-11-09 03:55:41 +00:00
int main ( int argc , char * * argv ) {
namespace po = boost : : program_options ;
const auto arg_wallet_file = wallet_args : : arg_wallet_file ( ) ;
const auto arg_from_json = wallet_args : : arg_generate_from_json ( ) ;
po : : options_description desc_params ( wallet_args : : tr ( " Wallet options " ) ) ;
tools : : wallet2 : : init_options ( desc_params ) ;
command_line : : add_arg ( desc_params , arg_rpc_bind_ip ) ;
command_line : : add_arg ( desc_params , arg_rpc_bind_port ) ;
2016-12-16 23:08:24 +00:00
command_line : : add_arg ( desc_params , arg_rpc_login ) ;
command_line : : add_arg ( desc_params , arg_disable_rpc_login ) ;
2016-11-28 23:39:59 +00:00
command_line : : add_arg ( desc_params , arg_confirm_external_bind ) ;
2016-11-09 03:55:41 +00:00
command_line : : add_arg ( desc_params , arg_wallet_file ) ;
command_line : : add_arg ( desc_params , arg_from_json ) ;
const auto vm = wallet_args : : main (
argc , argv ,
" monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>] [--rpc-bind-port=<port>] " ,
desc_params ,
po : : positional_options_description ( )
) ;
if ( ! vm )
{
return 1 ;
}
epee : : log_space : : log_singletone : : add_logger ( LOGGER_CONSOLE , NULL , NULL , LOG_LEVEL_2 ) ;
std : : unique_ptr < tools : : wallet2 > wal ;
try
{
const auto wallet_file = command_line : : get_arg ( * vm , arg_wallet_file ) ;
const auto from_json = command_line : : get_arg ( * vm , arg_from_json ) ;
if ( ! wallet_file . empty ( ) & & ! from_json . empty ( ) )
{
LOG_ERROR ( tools : : wallet_rpc_server : : tr ( " Can't specify more than one of --wallet-file and --generate-from-json " ) ) ;
return 1 ;
}
if ( wallet_file . empty ( ) & & from_json . empty ( ) )
{
LOG_ERROR ( tools : : wallet_rpc_server : : tr ( " Must specify --wallet-file or --generate-from-json " ) ) ;
return 1 ;
}
LOG_PRINT_L0 ( tools : : wallet_rpc_server : : tr ( " Loading wallet... " ) ) ;
if ( ! wallet_file . empty ( ) )
{
wal = tools : : wallet2 : : make_from_file ( * vm , wallet_file ) . first ;
}
else
{
wal = tools : : wallet2 : : make_from_json ( * vm , from_json ) ;
}
if ( ! wal )
{
return 1 ;
}
bool quit = false ;
tools : : signal_handler : : install ( [ & wal , & quit ] ( int ) {
assert ( wal ) ;
quit = true ;
wal - > stop ( ) ;
} ) ;
wal - > refresh ( ) ;
// if we ^C during potentially length load/refresh, there's no server loop yet
if ( quit )
{
LOG_PRINT_L0 ( tools : : wallet_rpc_server : : tr ( " Storing wallet... " ) ) ;
wal - > store ( ) ;
LOG_PRINT_GREEN ( tools : : wallet_rpc_server : : tr ( " Stored ok " ) , LOG_LEVEL_0 ) ;
return 1 ;
}
LOG_PRINT_GREEN ( tools : : wallet_rpc_server : : tr ( " Loaded ok " ) , LOG_LEVEL_0 ) ;
}
catch ( const std : : exception & e )
{
LOG_ERROR ( tools : : wallet_rpc_server : : tr ( " Wallet initialization failed: " ) < < e . what ( ) ) ;
return 1 ;
}
tools : : wallet_rpc_server wrpc ( * wal ) ;
bool r = wrpc . init ( * vm ) ;
CHECK_AND_ASSERT_MES ( r , 1 , tools : : wallet_rpc_server : : tr ( " Failed to initialize wallet rpc server " ) ) ;
tools : : signal_handler : : install ( [ & wrpc , & wal ] ( int ) {
wrpc . send_stop_signal ( ) ;
} ) ;
LOG_PRINT_L0 ( tools : : wallet_rpc_server : : tr ( " Starting wallet rpc server " ) ) ;
wrpc . run ( ) ;
LOG_PRINT_L0 ( tools : : wallet_rpc_server : : tr ( " Stopped wallet rpc server " ) ) ;
try
{
LOG_PRINT_L0 ( tools : : wallet_rpc_server : : tr ( " Storing wallet... " ) ) ;
wal - > store ( ) ;
LOG_PRINT_GREEN ( tools : : wallet_rpc_server : : tr ( " Stored ok " ) , LOG_LEVEL_0 ) ;
}
catch ( const std : : exception & e )
{
LOG_ERROR ( tools : : wallet_rpc_server : : tr ( " Failed to store wallet: " ) < < e . what ( ) ) ;
return 1 ;
}
return 0 ;
}