2015-12-31 08:39:56 +02:00
// Copyright (c) 2014-2016, The Monero Project
2014-07-23 16:03:52 +03: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 19:00:17 +03: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-18 01:15:21 +03:00
# include "wallet_rpc_server_commands_defs.h"
2014-04-02 19:00:17 +03:00
# include "misc_language.h"
2014-05-03 19:19:43 +03:00
# include "string_tools.h"
2014-04-02 19:00:17 +03: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 ( )
{
2015-12-29 01:38:30 +02:00
m_stop = false ;
2014-04-02 19:00:17 +03:00
m_net_server . add_idle_handler ( [ this ] ( ) {
2014-11-01 08:30:53 +02:00
try {
m_wallet . refresh ( ) ;
} catch ( const std : : exception & ex ) {
LOG_ERROR ( " Exception at while refreshing, what= " < < ex . what ( ) ) ;
}
2014-04-02 19:00:17 +03:00
return true ;
} , 20000 ) ;
2015-12-29 01:38:30 +02: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 19:00:17 +03: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 : : 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 ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 14:01:30 +02: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 19:00:17 +03: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 14:01:30 +02: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-25 01:20:46 +03:00
{
try
{
2014-09-09 17:58:53 +03:00
res . address = m_wallet . get_account ( ) . get_public_address_str ( m_wallet . testnet ( ) ) ;
2014-05-25 01:20:46 +03:00
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
2015-12-23 18:04:04 +02: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-25 01:20:46 +03:00
//------------------------------------------------------------------------------------------------------------------------------
2015-08-09 12:09:39 +03: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 19:00:17 +03:00
{
2015-08-09 12:09:39 +03:00
crypto : : hash8 integrated_payment_id = cryptonote : : null_hash8 ;
std : : string extra_nonce ;
2014-06-18 01:15:21 +03:00
for ( auto it = destinations . begin ( ) ; it ! = destinations . end ( ) ; it + + )
2014-04-02 19:00:17 +03:00
{
cryptonote : : tx_destination_entry de ;
2015-06-11 11:44:13 +03:00
bool has_payment_id ;
2015-08-09 12:09:39 +03:00
crypto : : hash8 new_payment_id ;
2015-06-11 11:44:13 +03:00
if ( ! get_account_integrated_address_from_str ( de . addr , has_payment_id , new_payment_id , m_wallet . testnet ( ) , it - > address ) )
2014-04-02 19:00:17 +03: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 11:44:13 +03:00
if ( has_payment_id )
{
2015-08-09 12:09:39 +03:00
if ( ! payment_id . empty ( ) | | integrated_payment_id ! = cryptonote : : null_hash8 )
2015-06-11 11:44:13 +03: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 12:09:39 +03:00
cryptonote : : set_encrypted_payment_id_to_tx_extra_nonce ( extra_nonce , integrated_payment_id ) ;
2016-07-10 15:45:01 +03: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 11:44:13 +03:00
}
2014-04-02 19:00:17 +03:00
}
2014-06-02 01:22:42 +03:00
2014-06-18 01:15:21 +03:00
if ( ! payment_id . empty ( ) )
{
2014-06-02 01:22:42 +03:00
/* Just to clarify */
2014-06-18 01:15:21 +03:00
const std : : string & payment_id_str = payment_id ;
2014-06-02 01:22:42 +03:00
2015-08-09 12:09:39 +03:00
crypto : : hash long_payment_id ;
crypto : : hash8 short_payment_id ;
2014-06-02 01:22:42 +03:00
/* Parse payment ID */
2015-08-09 12:09:39 +03: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-02 01:22:42 +03:00
er . code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID ;
2015-08-09 12:09:39 +03:00
er . message = " Payment id has invalid format: \" " + payment_id_str + " \" , expected 16 or 64 character string " ;
2014-06-02 01:22:42 +03: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 15:45:01 +03:00
er . message = " Something went wrong with payment_id. Please check its format: \" " + payment_id_str + " \" , expected 64-character string " ;
2014-06-02 01:22:42 +03:00
return false ;
}
}
2014-06-18 01:15:21 +03:00
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 14:01:30 +02: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-18 01:15:21 +03:00
{
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < uint8_t > extra ;
2015-01-11 13:06:35 +02:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-06-18 01:15:21 +03:00
// validate the transfer requested and populate dsts & extra
2015-08-09 12:09:39 +03:00
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
2014-06-18 01:15:21 +03:00
{
return false ;
}
try
{
2016-03-11 23:32:16 +02:00
uint64_t mixin = req . mixin ;
if ( mixin < 2 & & m_wallet . use_fork_rules ( 2 ) ) {
LOG_PRINT_L1 ( " Requested mixin " < < req . mixin < < " too low for hard fork 2, using 2 " ) ;
mixin = 2 ;
}
2016-06-23 00:21:30 +03:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions ( dsts , mixin , req . unlock_time , req . fee_multiplier , extra , req . trusted_daemon ) ;
2014-06-18 01:15:21 +03: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 23:16:00 +03:00
res . tx_hash = epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx_vector . back ( ) . tx ) ) ;
2015-08-19 22:59:44 +03:00
if ( req . get_tx_key )
2016-06-09 23:16:00 +03:00
res . tx_key = epee : : string_tools : : pod_to_hex ( ptx_vector . back ( ) . tx_key ) ;
2014-06-18 01:15:21 +03: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 14:01:30 +02: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-18 01:15:21 +03:00
{
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < uint8_t > extra ;
2015-01-11 13:06:35 +02:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-06-18 01:15:21 +03:00
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
2015-08-09 12:09:39 +03:00
if ( ! validate_transfer ( req . destinations , req . payment_id , dsts , extra , er ) )
2014-06-18 01:15:21 +03:00
{
return false ;
}
2014-06-02 01:22:42 +03:00
2014-04-02 19:00:17 +03:00
try
{
2016-03-11 23:32:16 +02:00
uint64_t mixin = req . mixin ;
if ( mixin < 2 & & m_wallet . use_fork_rules ( 2 ) ) {
LOG_PRINT_L1 ( " Requested mixin " < < req . mixin < < " too low for hard fork 2, using 2 " ) ;
mixin = 2 ;
}
2015-07-20 01:47:13 +03:00
std : : vector < wallet2 : : pending_tx > ptx_vector ;
if ( req . new_algorithm )
2016-06-23 00:21:30 +03:00
ptx_vector = m_wallet . create_transactions_2 ( dsts , mixin , req . unlock_time , req . fee_multiplier , extra , req . trusted_daemon ) ;
2015-07-20 01:47:13 +03:00
else
2016-06-23 00:21:30 +03:00
ptx_vector = m_wallet . create_transactions ( dsts , mixin , req . unlock_time , req . fee_multiplier , extra , req . trusted_daemon ) ;
2014-06-18 01:15:21 +03:00
m_wallet . commit_tx ( ptx_vector ) ;
2015-05-30 17:37:27 +03:00
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 23:16:00 +03:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2015-08-19 22:59:44 +03:00
if ( req . get_tx_keys )
2016-06-09 23:16:00 +03:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2015-05-30 17:37:27 +03: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-27 01:22:57 +02:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_unmixable_sweep_transactions ( req . trusted_daemon ) ;
2015-05-30 17:37:27 +03:00
m_wallet . commit_tx ( ptx_vector ) ;
2014-06-18 01:15:21 +03:00
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 23:16:00 +03:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2015-08-19 22:59:44 +03:00
if ( req . get_tx_keys )
2016-06-09 23:16:00 +03:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2014-06-18 01:15:21 +03:00
}
2014-04-02 19:00:17 +03: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 19:19:43 +03:00
return false ;
2014-04-02 19:00:17 +03:00
}
catch ( const std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR ;
er . message = e . what ( ) ;
2014-05-03 19:19:43 +03:00
return false ;
2014-04-02 19:00:17 +03:00
}
catch ( . . . )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
2016-04-19 23:20:27 +03: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-06-23 00:21:30 +03:00
std : : vector < wallet2 : : pending_tx > ptx_vector = m_wallet . create_transactions_all ( dsts [ 0 ] . addr , req . mixin , req . unlock_time , req . fee_multiplier , extra , req . trusted_daemon ) ;
2016-04-19 23:20:27 +03:00
m_wallet . commit_tx ( ptx_vector ) ;
// populate response with tx hashes
for ( auto & ptx : ptx_vector )
{
2016-06-09 23:16:00 +03:00
res . tx_hash_list . push_back ( epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( ptx . tx ) ) ) ;
2016-04-19 23:20:27 +03:00
if ( req . get_tx_keys )
2016-06-09 23:16:00 +03:00
res . tx_key_list . push_back ( epee : : string_tools : : pod_to_hex ( ptx . tx_key ) ) ;
2016-04-19 23:20:27 +03: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 19:00:17 +03:00
er . message = " WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR " ;
2014-05-03 19:19:43 +03:00
return false ;
2014-04-02 19:00:17 +03:00
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-06-13 18:08:00 +03: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 12:09:39 +03:00
crypto : : hash8 payment_id ;
2015-06-13 18:08:00 +03:00
if ( req . payment_id . empty ( ) )
{
2015-08-26 10:28:58 +03:00
payment_id = crypto : : rand < crypto : : hash8 > ( ) ;
2015-06-13 18:08:00 +03:00
}
else
{
2015-08-09 12:09:39 +03:00
if ( ! tools : : wallet2 : : parse_short_payment_id ( req . payment_id , payment_id ) )
2015-06-13 18:08:00 +03: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 ( ) ) ;
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 12:09:39 +03:00
crypto : : hash8 payment_id ;
2015-06-13 18:08:00 +03: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 23:16:00 +03:00
res . payment_id = epee : : string_tools : : pod_to_hex ( payment_id ) ;
2015-06-13 18:08:00 +03: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 14:01:30 +02: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 19:00:17 +03:00
{
2015-01-11 13:06:35 +02:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-04-02 19:00:17 +03: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 14:01:30 +02: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 19:19:43 +03:00
{
crypto : : hash payment_id ;
2016-03-05 21:30:48 +02:00
crypto : : hash8 payment_id8 ;
2014-05-03 19:19:43 +03: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 ;
er . message = " Payment ID has invald format " ;
return false ;
}
2016-03-05 21:30:48 +02: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 19:19:43 +03:00
res . payments . clear ( ) ;
std : : list < wallet2 : : payment_details > payment_list ;
m_wallet . get_payments ( payment_id , payment_list ) ;
2014-07-22 19:00:25 +03:00
for ( auto & payment : payment_list )
2014-05-03 19:19:43 +03:00
{
wallet_rpc : : payment_details rpc_payment ;
2014-07-22 19:00:25 +03:00
rpc_payment . payment_id = req . payment_id ;
2014-05-03 19:19:43 +03: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 14:01:30 +02: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 19:00:25 +03:00
{
res . payments . clear ( ) ;
2015-01-10 21:32:08 +02: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 19:00:25 +03:00
for ( auto & payment_id_str : req . payment_ids )
{
crypto : : hash payment_id ;
2015-08-09 12:09:39 +03:00
crypto : : hash8 payment_id8 ;
2014-07-22 19:00:25 +03: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 12:09:39 +03: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 19:00:25 +03: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 14:01:30 +02: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 13:52:11 +03: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 23:36:35 +03:00
er . message = " Transfer type must be one of: all, available, or unavailable " ;
2014-05-27 13:52:11 +03: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-20 00:57:26 +02:00
auto txBlob = t_serializable_object_to_blob ( td . m_tx ) ;
2014-05-27 13:52:11 +03: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-06-09 23:16:00 +03:00
rpc_transfers . tx_hash = epee : : string_tools : : pod_to_hex ( cryptonote : : get_transaction_hash ( td . m_tx ) ) ;
2015-02-20 00:57:26 +02:00
rpc_transfers . tx_size = txBlob . size ( ) ;
2014-05-27 13:52:11 +03:00
res . transfers . push_back ( rpc_transfers ) ;
}
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-03-27 14:01:30 +02: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 09:17:23 +03:00
{
2015-01-11 13:06:35 +02:00
if ( m_wallet . restricted ( ) )
{
er . code = WALLET_RPC_ERROR_CODE_DENIED ;
er . message = " Command unavailable in restricted mode. " ;
return false ;
}
2014-08-05 09:17:23 +03: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 09:57:08 +03: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 09:17:23 +03:00
else
{
er . message = " key_type " + req . key_type + " not found " ;
return false ;
}
return true ;
}
2015-12-30 14:58:15 +02: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 23:46:48 +03: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 16:53:37 +02: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-29 01:38:30 +02:00
m_stop . store ( true , std : : memory_order_relaxed ) ;
2015-12-05 16:53:37 +02:00
}
catch ( std : : exception & e )
{
er . code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR ;
er . message = e . what ( ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-04-20 20:19:42 +03: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-27 00:39:08 +03: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-28 01:43:39 +03: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-27 00:39:08 +03:00
if ( req . in )
{
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > payments ;
2016-04-28 01:43:39 +03:00
m_wallet . get_payments ( payments , min_height , max_height ) ;
2016-04-27 00:39:08 +03:00
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : payment_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
res . in . push_back ( wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry ( ) ) ;
wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry & entry = res . in . back ( ) ;
const tools : : wallet2 : : payment_details & pd = i - > second ;
entry . txid = string_tools : : pod_to_hex ( pd . m_tx_hash ) ;
entry . payment_id = string_tools : : pod_to_hex ( i - > first ) ;
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 ) ;
}
}
if ( req . out )
{
std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > payments ;
2016-04-28 01:43:39 +03:00
m_wallet . get_payments_out ( payments , min_height , max_height ) ;
2016-04-27 00:39:08 +03:00
for ( std : : list < std : : pair < crypto : : hash , tools : : wallet2 : : confirmed_transfer_details > > : : const_iterator i = payments . begin ( ) ; i ! = payments . end ( ) ; + + i ) {
res . in . push_back ( wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry ( ) ) ;
wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry & entry = res . in . back ( ) ;
const tools : : wallet2 : : confirmed_transfer_details & pd = i - > second ;
entry . txid = string_tools : : pod_to_hex ( i - > first ) ;
entry . payment_id = string_tools : : pod_to_hex ( i - > second . 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 ( i - > first ) ;
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 ) ;
}
}
}
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 ;
std : : list < wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry > & entries = is_failed ? res . failed : res . pending ;
entries . push_back ( wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry ( ) ) ;
wallet_rpc : : COMMAND_RPC_GET_TRANSFERS : : entry & entry = entries . back ( ) ;
entry . txid = string_tools : : pod_to_hex ( i - > first ) ;
entry . payment_id = string_tools : : pod_to_hex ( i - > second . m_payment_id ) ;
entry . payment_id = string_tools : : pod_to_hex ( i - > second . 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 ;
uint64_t amount = 0 ;
cryptonote : : get_inputs_money_amount ( pd . m_tx , amount ) ;
entry . fee = amount - get_outs_money_amount ( pd . m_tx ) ;
entry . amount = amount - pd . m_change - entry . fee ;
entry . note = m_wallet . get_tx_note ( i - > first ) ;
}
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-03 19:19:43 +03:00
}
2015-12-23 18:04:04 +02:00