@ -69,6 +69,7 @@ using namespace epee;
# include "common/base58.h"
# include "common/base58.h"
# include "common/dns_utils.h"
# include "common/dns_utils.h"
# include "common/notify.h"
# include "common/notify.h"
# include "common/perf_timer.h"
# include "ringct/rctSigs.h"
# include "ringct/rctSigs.h"
# include "ringdb.h"
# include "ringdb.h"
# include "device/device_cold.hpp"
# include "device/device_cold.hpp"
@ -112,11 +113,11 @@ using namespace cryptonote;
# define SUBADDRESS_LOOKAHEAD_MAJOR 50
# define SUBADDRESS_LOOKAHEAD_MAJOR 50
# define SUBADDRESS_LOOKAHEAD_MINOR 200
# define SUBADDRESS_LOOKAHEAD_MINOR 200
# define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\00 2 "
# define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\00 3 "
# define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
# define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
# define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\00 3 "
# define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\00 4 "
# define SEGREGATION_FORK_HEIGHT 99999999
# define SEGREGATION_FORK_HEIGHT 99999999
# define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
# define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
@ -1605,6 +1606,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td . m_txid = txid ;
td . m_txid = txid ;
td . m_key_image = tx_scan_info [ o ] . ki ;
td . m_key_image = tx_scan_info [ o ] . ki ;
td . m_key_image_known = ! m_watch_only & & ! m_multisig ;
td . m_key_image_known = ! m_watch_only & & ! m_multisig ;
td . m_key_image_requested = false ;
td . m_key_image_partial = m_multisig ;
td . m_key_image_partial = m_multisig ;
td . m_amount = amount ;
td . m_amount = amount ;
td . m_pk_index = pk_index - 1 ;
td . m_pk_index = pk_index - 1 ;
@ -5465,7 +5467,7 @@ std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) c
txs . txes . push_back ( get_construction_data_with_decrypted_short_payment_id ( tx , m_account . get_device ( ) ) ) ;
txs . txes . push_back ( get_construction_data_with_decrypted_short_payment_id ( tx , m_account . get_device ( ) ) ) ;
}
}
txs . transfers = m_transfers ;
txs . transfers = export_outputs( ) ;
// save as binary
// save as binary
std : : ostringstream oss ;
std : : ostringstream oss ;
boost : : archive : : portable_binary_oarchive ar ( oss ) ;
boost : : archive : : portable_binary_oarchive ar ( oss ) ;
@ -7951,6 +7953,7 @@ void wallet2::light_wallet_get_unspent_outs()
td . m_key_image = unspent_key_image ;
td . m_key_image = unspent_key_image ;
td . m_key_image_known = ! m_watch_only & & ! m_multisig ;
td . m_key_image_known = ! m_watch_only & & ! m_multisig ;
td . m_key_image_requested = false ;
td . m_key_image_partial = m_multisig ;
td . m_key_image_partial = m_multisig ;
td . m_amount = o . amount ;
td . m_amount = o . amount ;
td . m_pk_index = 0 ;
td . m_pk_index = 0 ;
@ -9124,7 +9127,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
{
{
txs . txes . push_back ( get_construction_data_with_decrypted_short_payment_id ( tx , m_account . get_device ( ) ) ) ;
txs . txes . push_back ( get_construction_data_with_decrypted_short_payment_id ( tx , m_account . get_device ( ) ) ) ;
}
}
txs . transfers = m_transfers;
txs . transfers = std: : make_pair ( 0 , m_transfers) ;
auto dev_cold = dynamic_cast < : : hw : : device_cold * > ( & hwdev ) ;
auto dev_cold = dynamic_cast < : : hw : : device_cold * > ( & hwdev ) ;
CHECK_AND_ASSERT_THROW_MES ( dev_cold , " Device does not implement cold signing interface " ) ;
CHECK_AND_ASSERT_THROW_MES ( dev_cold , " Device does not implement cold signing interface " ) ;
@ -9155,7 +9158,7 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
dev_cold - > ki_sync ( & wallet_shim , m_transfers , ski ) ;
dev_cold - > ki_sync ( & wallet_shim , m_transfers , ski ) ;
return import_key_images ( ski , spent , unspent ) ;
return import_key_images ( ski , 0 , spent , unspent ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
void wallet2 : : get_hard_fork_info ( uint8_t version , uint64_t & earliest_height ) const
void wallet2 : : get_hard_fork_info ( uint8_t version , uint64_t & earliest_height ) const
@ -10517,31 +10520,45 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
bool wallet2 : : export_key_images ( const std : : string & filename ) const
bool wallet2 : : export_key_images ( const std : : string & filename ) const
{
{
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > ski = export_key_images ( ) ;
PERF_TIMER ( export_key_images ) ;
std : : pair < size_t , std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > > ski = export_key_images ( ) ;
std : : string magic ( KEY_IMAGE_EXPORT_FILE_MAGIC , strlen ( KEY_IMAGE_EXPORT_FILE_MAGIC ) ) ;
std : : string magic ( KEY_IMAGE_EXPORT_FILE_MAGIC , strlen ( KEY_IMAGE_EXPORT_FILE_MAGIC ) ) ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
const uint32_t offset = ski . first ;
std : : string data ;
std : : string data ;
data . reserve ( 4 + ski . second . size ( ) * ( sizeof ( crypto : : key_image ) + sizeof ( crypto : : signature ) ) + 2 * sizeof ( crypto : : public_key ) ) ;
data . resize ( 4 ) ;
data [ 0 ] = offset & 0xff ;
data [ 1 ] = ( offset > > 8 ) & 0xff ;
data [ 2 ] = ( offset > > 16 ) & 0xff ;
data [ 3 ] = ( offset > > 24 ) & 0xff ;
data + = std : : string ( ( const char * ) & keys . m_spend_public_key , sizeof ( crypto : : public_key ) ) ;
data + = std : : string ( ( const char * ) & keys . m_spend_public_key , sizeof ( crypto : : public_key ) ) ;
data + = std : : string ( ( const char * ) & keys . m_view_public_key , sizeof ( crypto : : public_key ) ) ;
data + = std : : string ( ( const char * ) & keys . m_view_public_key , sizeof ( crypto : : public_key ) ) ;
for ( const auto & i : ski )
for ( const auto & i : ski .second )
{
{
data + = std : : string ( ( const char * ) & i . first , sizeof ( crypto : : key_image ) ) ;
data + = std : : string ( ( const char * ) & i . first , sizeof ( crypto : : key_image ) ) ;
data + = std : : string ( ( const char * ) & i . second , sizeof ( crypto : : signature ) ) ;
data + = std : : string ( ( const char * ) & i . second , sizeof ( crypto : : signature ) ) ;
}
}
// encrypt data, keep magic plaintext
// encrypt data, keep magic plaintext
PERF_TIMER ( export_key_images_encrypt ) ;
std : : string ciphertext = encrypt_with_view_secret_key ( data ) ;
std : : string ciphertext = encrypt_with_view_secret_key ( data ) ;
return epee : : file_io_utils : : save_string_to_file ( filename , magic + ciphertext ) ;
return epee : : file_io_utils : : save_string_to_file ( filename , magic + ciphertext ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > wallet2 : : export_key_images ( ) const
std : : pair < size_t , std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > > wallet2 : : export_key_images ( ) const
{
{
PERF_TIMER ( export_key_images_raw ) ;
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > ski ;
std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > ski ;
ski . reserve ( m_transfers . size ( ) ) ;
size_t offset = 0 ;
for ( size_t n = 0 ; n < m_transfers . size ( ) ; + + n )
while ( offset < m_transfers . size ( ) & & ! m_transfers [ offset ] . m_key_image_requested )
+ + offset ;
ski . reserve ( m_transfers . size ( ) - offset ) ;
for ( size_t n = offset ; n < m_transfers . size ( ) ; + + n )
{
{
const transfer_details & td = m_transfers [ n ] ;
const transfer_details & td = m_transfers [ n ] ;
@ -10585,11 +10602,12 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key
ski . push_back ( std : : make_pair ( td . m_key_image , signature ) ) ;
ski . push_back ( std : : make_pair ( td . m_key_image , signature ) ) ;
}
}
return s ki;
return s td: : make_pair ( offset , s ki) ;
}
}
uint64_t wallet2 : : import_key_images ( const std : : string & filename , uint64_t & spent , uint64_t & unspent )
uint64_t wallet2 : : import_key_images ( const std : : string & filename , uint64_t & spent , uint64_t & unspent )
{
{
PERF_TIMER ( import_key_images_fsu ) ;
std : : string data ;
std : : string data ;
bool r = epee : : file_io_utils : : load_file_to_string ( filename , data ) ;
bool r = epee : : file_io_utils : : load_file_to_string ( filename , data ) ;
@ -10603,6 +10621,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
try
try
{
{
PERF_TIMER ( import_key_images_decrypt ) ;
data = decrypt_with_view_secret_key ( std : : string ( data , magiclen ) ) ;
data = decrypt_with_view_secret_key ( std : : string ( data , magiclen ) ) ;
}
}
catch ( const std : : exception & e )
catch ( const std : : exception & e )
@ -10610,15 +10629,17 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
THROW_WALLET_EXCEPTION ( error : : wallet_internal_error , std : : string ( " Failed to decrypt " ) + filename + " : " + e . what ( ) ) ;
THROW_WALLET_EXCEPTION ( error : : wallet_internal_error , std : : string ( " Failed to decrypt " ) + filename + " : " + e . what ( ) ) ;
}
}
const size_t headerlen = 2 * sizeof ( crypto : : public_key ) ;
const size_t headerlen = 4 + 2 * sizeof ( crypto : : public_key ) ;
THROW_WALLET_EXCEPTION_IF ( data . size ( ) < headerlen , error : : wallet_internal_error , std : : string ( " Bad data size from file " ) + filename ) ;
THROW_WALLET_EXCEPTION_IF ( data . size ( ) < headerlen , error : : wallet_internal_error , std : : string ( " Bad data size from file " ) + filename ) ;
const crypto : : public_key & public_spend_key = * ( const crypto : : public_key * ) & data [ 0 ] ;
const uint32_t offset = ( uint8_t ) data [ 0 ] | ( ( ( uint8_t ) data [ 1 ] ) < < 8 ) | ( ( ( uint8_t ) data [ 2 ] ) < < 16 ) | ( ( ( uint8_t ) data [ 3 ] ) < < 24 ) ;
const crypto : : public_key & public_view_key = * ( const crypto : : public_key * ) & data [ sizeof ( crypto : : public_key ) ] ;
const crypto : : public_key & public_spend_key = * ( const crypto : : public_key * ) & data [ 4 ] ;
const crypto : : public_key & public_view_key = * ( const crypto : : public_key * ) & data [ 4 + sizeof ( crypto : : public_key ) ] ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
if ( public_spend_key ! = keys . m_spend_public_key | | public_view_key ! = keys . m_view_public_key )
if ( public_spend_key ! = keys . m_spend_public_key | | public_view_key ! = keys . m_view_public_key )
{
{
THROW_WALLET_EXCEPTION ( error : : wallet_internal_error , std : : string ( " Key images from " ) + filename + " are for a different account " ) ;
THROW_WALLET_EXCEPTION ( error : : wallet_internal_error , std : : string ( " Key images from " ) + filename + " are for a different account " ) ;
}
}
THROW_WALLET_EXCEPTION_IF ( offset > m_transfers . size ( ) , error : : wallet_internal_error , " Offset larger than known outputs " ) ;
const size_t record_size = sizeof ( crypto : : key_image ) + sizeof ( crypto : : signature ) ;
const size_t record_size = sizeof ( crypto : : key_image ) + sizeof ( crypto : : signature ) ;
THROW_WALLET_EXCEPTION_IF ( ( data . size ( ) - headerlen ) % record_size ,
THROW_WALLET_EXCEPTION_IF ( ( data . size ( ) - headerlen ) % record_size ,
@ -10635,28 +10656,33 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
ski . push_back ( std : : make_pair ( key_image , signature ) ) ;
ski . push_back ( std : : make_pair ( key_image , signature ) ) ;
}
}
return import_key_images ( ski , spent, unspent ) ;
return import_key_images ( ski , offset, spent, unspent ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : import_key_images ( const std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > & signed_key_images , uint64_t & spent , uint64_t & unspent , bool check_spent )
uint64_t wallet2 : : import_key_images ( const std : : vector < std : : pair < crypto : : key_image , crypto : : signature > > & signed_key_images , size_t offset , uint64_t & spent , uint64_t & unspent , bool check_spent )
{
{
PERF_TIMER ( import_key_images_lots ) ;
COMMAND_RPC_IS_KEY_IMAGE_SPENT : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_IS_KEY_IMAGE_SPENT : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_IS_KEY_IMAGE_SPENT : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
COMMAND_RPC_IS_KEY_IMAGE_SPENT : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
THROW_WALLET_EXCEPTION_IF ( signed_key_images . size ( ) > m_transfers . size ( ) , error : : wallet_internal_error ,
THROW_WALLET_EXCEPTION_IF ( offset > m_transfers . size ( ) , error : : wallet_internal_error , " Offset larger than known outputs " ) ;
THROW_WALLET_EXCEPTION_IF ( signed_key_images . size ( ) > m_transfers . size ( ) - offset , error : : wallet_internal_error ,
" The blockchain is out of date compared to the signed key images " ) ;
" The blockchain is out of date compared to the signed key images " ) ;
if ( signed_key_images . empty ( ) )
if ( signed_key_images . empty ( ) & & offset = = 0 )
{
{
spent = 0 ;
spent = 0 ;
unspent = 0 ;
unspent = 0 ;
return 0 ;
return 0 ;
}
}
req . key_images . reserve ( signed_key_images . size ( ) ) ;
PERF_TIMER_START ( import_key_images_A ) ;
for ( size_t n = 0 ; n < signed_key_images . size ( ) ; + + n )
for ( size_t n = 0 ; n < signed_key_images . size ( ) ; + + n )
{
{
const transfer_details & td = m_transfers [ n ] ;
const transfer_details & td = m_transfers [ n + offset ] ;
const crypto : : key_image & key_image = signed_key_images [ n ] . first ;
const crypto : : key_image & key_image = signed_key_images [ n ] . first ;
const crypto : : signature & signature = signed_key_images [ n ] . second ;
const crypto : : signature & signature = signed_key_images [ n ] . second ;
@ -10667,30 +10693,37 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const cryptonote : : txout_to_key & o = boost : : get < cryptonote : : txout_to_key > ( out . target ) ;
const cryptonote : : txout_to_key & o = boost : : get < cryptonote : : txout_to_key > ( out . target ) ;
const crypto : : public_key pkey = o . key ;
const crypto : : public_key pkey = o . key ;
if ( ! td . m_key_image_known | | ! ( key_image = = td . m_key_image ) )
{
std : : vector < const crypto : : public_key * > pkeys ;
std : : vector < const crypto : : public_key * > pkeys ;
pkeys . push_back ( & pkey ) ;
pkeys . push_back ( & pkey ) ;
THROW_WALLET_EXCEPTION_IF ( ! ( rct : : scalarmultKey ( rct : : ki2rct ( key_image ) , rct : : curveOrder ( ) ) = = rct : : identity ( ) ) ,
THROW_WALLET_EXCEPTION_IF ( ! ( rct : : scalarmultKey ( rct : : ki2rct ( key_image ) , rct : : curveOrder ( ) ) = = rct : : identity ( ) ) ,
error : : wallet_internal_error , " Key image out of validity domain: input " + boost : : lexical_cast < std : : string > ( n ) + " / "
error : : wallet_internal_error , " Key image out of validity domain: input " + boost : : lexical_cast < std : : string > ( n + offset ) + " / "
+ boost : : lexical_cast < std : : string > ( signed_key_images . size ( ) ) + " , key image " + epee : : string_tools : : pod_to_hex ( key_image ) ) ;
+ boost : : lexical_cast < std : : string > ( signed_key_images . size ( ) ) + " , key image " + epee : : string_tools : : pod_to_hex ( key_image ) ) ;
THROW_WALLET_EXCEPTION_IF ( ! crypto : : check_ring_signature ( ( const crypto : : hash & ) key_image , key_image , pkeys , & signature ) ,
THROW_WALLET_EXCEPTION_IF ( ! crypto : : check_ring_signature ( ( const crypto : : hash & ) key_image , key_image , pkeys , & signature ) ,
error : : signature_check_failed , boost : : lexical_cast < std : : string > ( n ) + " / "
error : : signature_check_failed , boost : : lexical_cast < std : : string > ( n + offset ) + " / "
+ boost : : lexical_cast < std : : string > ( signed_key_images . size ( ) ) + " , key image " + epee : : string_tools : : pod_to_hex ( key_image )
+ boost : : lexical_cast < std : : string > ( signed_key_images . size ( ) ) + " , key image " + epee : : string_tools : : pod_to_hex ( key_image )
+ " , signature " + epee : : string_tools : : pod_to_hex ( signature ) + " , pubkey " + epee : : string_tools : : pod_to_hex ( * pkeys [ 0 ] ) ) ;
+ " , signature " + epee : : string_tools : : pod_to_hex ( signature ) + " , pubkey " + epee : : string_tools : : pod_to_hex ( * pkeys [ 0 ] ) ) ;
}
req . key_images . push_back ( epee : : string_tools : : pod_to_hex ( key_image ) ) ;
req . key_images . push_back ( epee : : string_tools : : pod_to_hex ( key_image ) ) ;
}
}
PERF_TIMER_STOP ( import_key_images_A ) ;
PERF_TIMER_START ( import_key_images_B ) ;
for ( size_t n = 0 ; n < signed_key_images . size ( ) ; + + n )
for ( size_t n = 0 ; n < signed_key_images . size ( ) ; + + n )
{
{
m_transfers [ n ] . m_key_image = signed_key_images [ n ] . first ;
m_transfers [ n + offset ] . m_key_image = signed_key_images [ n ] . first ;
m_key_images [ m_transfers [ n ] . m_key_image ] = n ;
m_key_images [ m_transfers [ n + offset ] . m_key_image ] = n + offset ;
m_transfers [ n ] . m_key_image_known = true ;
m_transfers [ n + offset ] . m_key_image_known = true ;
m_transfers [ n ] . m_key_image_partial = false ;
m_transfers [ n + offset ] . m_key_image_requested = false ;
m_transfers [ n + offset ] . m_key_image_partial = false ;
}
}
PERF_TIMER_STOP ( import_key_images_B ) ;
if ( check_spent )
if ( check_spent )
{
{
PERF_TIMER ( import_key_images_RPC ) ;
m_daemon_rpc_mutex . lock ( ) ;
m_daemon_rpc_mutex . lock ( ) ;
bool r = epee : : net_utils : : invoke_http_json ( " /is_key_image_spent " , req , daemon_resp , m_http_client , rpc_timeout ) ;
bool r = epee : : net_utils : : invoke_http_json ( " /is_key_image_spent " , req , daemon_resp , m_http_client , rpc_timeout ) ;
m_daemon_rpc_mutex . unlock ( ) ;
m_daemon_rpc_mutex . unlock ( ) ;
@ -10702,7 +10735,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std : : to_string ( daemon_resp . spent_status . size ( ) ) + " , expected " + std : : to_string ( signed_key_images . size ( ) ) ) ;
std : : to_string ( daemon_resp . spent_status . size ( ) ) + " , expected " + std : : to_string ( signed_key_images . size ( ) ) ) ;
for ( size_t n = 0 ; n < daemon_resp . spent_status . size ( ) ; + + n )
for ( size_t n = 0 ; n < daemon_resp . spent_status . size ( ) ; + + n )
{
{
transfer_details & td = m_transfers [ n ] ;
transfer_details & td = m_transfers [ n + offset ] ;
td . m_spent = daemon_resp . spent_status [ n ] ! = COMMAND_RPC_IS_KEY_IMAGE_SPENT : : UNSPENT ;
td . m_spent = daemon_resp . spent_status [ n ] ! = COMMAND_RPC_IS_KEY_IMAGE_SPENT : : UNSPENT ;
}
}
}
}
@ -10713,6 +10746,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
// was created by sweep_all, so we can't know the spent height and other detailed info.
// was created by sweep_all, so we can't know the spent height and other detailed info.
std : : unordered_map < crypto : : key_image , crypto : : hash > spent_key_images ;
std : : unordered_map < crypto : : key_image , crypto : : hash > spent_key_images ;
PERF_TIMER_START ( import_key_images_C ) ;
for ( const transfer_details & td : m_transfers )
for ( const transfer_details & td : m_transfers )
{
{
for ( const cryptonote : : txin_v & in : td . m_tx . vin )
for ( const cryptonote : : txin_v & in : td . m_tx . vin )
@ -10721,10 +10755,12 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
spent_key_images . insert ( std : : make_pair ( boost : : get < cryptonote : : txin_to_key > ( in ) . k_image , td . m_txid ) ) ;
spent_key_images . insert ( std : : make_pair ( boost : : get < cryptonote : : txin_to_key > ( in ) . k_image , td . m_txid ) ) ;
}
}
}
}
PERF_TIMER_STOP ( import_key_images_C ) ;
PERF_TIMER_START ( import_key_images_D ) ;
for ( size_t i = 0 ; i < signed_key_images . size ( ) ; + + i )
for ( size_t i = 0 ; i < signed_key_images . size ( ) ; + + i )
{
{
transfer_details & td = m_transfers [ i ] ;
const transfer_details & td = m_transfers [ i + offset ] ;
uint64_t amount = td . amount ( ) ;
uint64_t amount = td . amount ( ) ;
if ( td . m_spent )
if ( td . m_spent )
spent + = amount ;
spent + = amount ;
@ -10742,6 +10778,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
spent_txids . insert ( skii - > second ) ;
spent_txids . insert ( skii - > second ) ;
}
}
}
}
PERF_TIMER_STOP ( import_key_images_D ) ;
MDEBUG ( " Total: " < < print_money ( spent ) < < " spent, " < < print_money ( unspent ) < < " unspent " ) ;
MDEBUG ( " Total: " < < print_money ( spent ) < < " spent, " < < print_money ( unspent ) < < " unspent " ) ;
if ( check_spent )
if ( check_spent )
@ -10751,8 +10789,12 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
COMMAND_RPC_GET_TRANSACTIONS : : response gettxs_res ;
COMMAND_RPC_GET_TRANSACTIONS : : response gettxs_res ;
gettxs_req . decode_as_json = false ;
gettxs_req . decode_as_json = false ;
gettxs_req . prune = false ;
gettxs_req . prune = false ;
gettxs_req . txs_hashes . reserve ( spent_txids . size ( ) ) ;
for ( const crypto : : hash & spent_txid : spent_txids )
for ( const crypto : : hash & spent_txid : spent_txids )
gettxs_req . txs_hashes . push_back ( epee : : string_tools : : pod_to_hex ( spent_txid ) ) ;
gettxs_req . txs_hashes . push_back ( epee : : string_tools : : pod_to_hex ( spent_txid ) ) ;
PERF_TIMER_START ( import_key_images_E ) ;
m_daemon_rpc_mutex . lock ( ) ;
m_daemon_rpc_mutex . lock ( ) ;
bool r = epee : : net_utils : : invoke_http_json ( " /gettransactions " , gettxs_req , gettxs_res , m_http_client , rpc_timeout ) ;
bool r = epee : : net_utils : : invoke_http_json ( " /gettransactions " , gettxs_req , gettxs_res , m_http_client , rpc_timeout ) ;
m_daemon_rpc_mutex . unlock ( ) ;
m_daemon_rpc_mutex . unlock ( ) ;
@ -10760,8 +10802,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
THROW_WALLET_EXCEPTION_IF ( gettxs_res . status = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " gettransactions " ) ;
THROW_WALLET_EXCEPTION_IF ( gettxs_res . status = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " gettransactions " ) ;
THROW_WALLET_EXCEPTION_IF ( gettxs_res . txs . size ( ) ! = spent_txids . size ( ) , error : : wallet_internal_error ,
THROW_WALLET_EXCEPTION_IF ( gettxs_res . txs . size ( ) ! = spent_txids . size ( ) , error : : wallet_internal_error ,
" daemon returned wrong response for gettransactions, wrong count = " + std : : to_string ( gettxs_res . txs . size ( ) ) + " , expected " + std : : to_string ( spent_txids . size ( ) ) ) ;
" daemon returned wrong response for gettransactions, wrong count = " + std : : to_string ( gettxs_res . txs . size ( ) ) + " , expected " + std : : to_string ( spent_txids . size ( ) ) ) ;
PERF_TIMER_STOP ( import_key_images_E ) ;
// process each outgoing tx
// process each outgoing tx
PERF_TIMER_START ( import_key_images_F ) ;
auto spent_txid = spent_txids . begin ( ) ;
auto spent_txid = spent_txids . begin ( ) ;
hw : : device & hwdev = m_account . get_device ( ) ;
hw : : device & hwdev = m_account . get_device ( ) ;
for ( const COMMAND_RPC_GET_TRANSACTIONS : : entry & e : gettxs_res . txs )
for ( const COMMAND_RPC_GET_TRANSACTIONS : : entry & e : gettxs_res . txs )
@ -10857,7 +10901,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
+ + spent_txid ;
+ + spent_txid ;
}
}
PERF_TIMER_STOP ( import_key_images_F ) ;
PERF_TIMER_START ( import_key_images_G ) ;
for ( size_t n : swept_transfers )
for ( size_t n : swept_transfers )
{
{
const transfer_details & td = m_transfers [ n ] ;
const transfer_details & td = m_transfers [ n ] ;
@ -10868,6 +10914,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const crypto : : hash & spent_txid = crypto : : null_hash ; // spent txid is unknown
const crypto : : hash & spent_txid = crypto : : null_hash ; // spent txid is unknown
m_confirmed_txs . insert ( std : : make_pair ( spent_txid , pd ) ) ;
m_confirmed_txs . insert ( std : : make_pair ( spent_txid , pd ) ) ;
}
}
PERF_TIMER_STOP ( import_key_images_G ) ;
}
}
return m_transfers [ signed_key_images . size ( ) - 1 ] . m_block_height ;
return m_transfers [ signed_key_images . size ( ) - 1 ] . m_block_height ;
@ -10888,6 +10935,7 @@ bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
td . m_key_image = key_images [ i ] ;
td . m_key_image = key_images [ i ] ;
m_key_images [ m_transfers [ i ] . m_key_image ] = i ;
m_key_images [ m_transfers [ i ] . m_key_image ] = i ;
td . m_key_image_known = true ;
td . m_key_image_known = true ;
td . m_key_image_requested = false ;
td . m_key_image_partial = false ;
td . m_key_image_partial = false ;
m_pub_keys [ m_transfers [ i ] . get_public_key ( ) ] = i ;
m_pub_keys [ m_transfers [ i ] . get_public_key ( ) ] = i ;
}
}
@ -10953,50 +11001,86 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
m_last_block_reward = cryptonote : : get_outs_money_amount ( genesis . miner_tx ) ;
m_last_block_reward = cryptonote : : get_outs_money_amount ( genesis . miner_tx ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
std : : vector < tools : : wallet2 : : transfer_details > wallet2 : : export_outputs ( ) const
std : : pair < size_t , std : : vector < tools : : wallet2 : : transfer_details > > wallet2 : : export_outputs ( ) const
{
{
PERF_TIMER ( export_outputs ) ;
std : : vector < tools : : wallet2 : : transfer_details > outs ;
std : : vector < tools : : wallet2 : : transfer_details > outs ;
outs . reserve ( m_transfers . size ( ) ) ;
size_t offset = 0 ;
for ( size_t n = 0 ; n < m_transfers . size ( ) ; + + n )
while ( offset < m_transfers . size ( ) & & m_transfers [ offset ] . m_key_image_known )
+ + offset ;
outs . reserve ( m_transfers . size ( ) - offset ) ;
for ( size_t n = offset ; n < m_transfers . size ( ) ; + + n )
{
{
const transfer_details & td = m_transfers [ n ] ;
const transfer_details & td = m_transfers [ n ] ;
outs . push_back ( td ) ;
outs . push_back ( td ) ;
}
}
return outs;
return std: : make_pair ( offset , outs) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : export_outputs_to_str ( ) const
std : : string wallet2 : : export_outputs_to_str ( ) const
{
{
std: : vector < tools : : wallet2 : : transfer_details > outs = export_outputs ( ) ;
PERF_TIMER( export_outputs_to_str ) ;
std : : stringstream oss ;
std : : stringstream oss ;
boost : : archive : : portable_binary_oarchive ar ( oss ) ;
boost : : archive : : portable_binary_oarchive ar ( oss ) ;
ar < < outs;
ar < < export_ output s( ) ;
std : : string magic ( OUTPUT_EXPORT_FILE_MAGIC , strlen ( OUTPUT_EXPORT_FILE_MAGIC ) ) ;
std : : string magic ( OUTPUT_EXPORT_FILE_MAGIC , strlen ( OUTPUT_EXPORT_FILE_MAGIC ) ) ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
const cryptonote : : account_public_address & keys = get_account ( ) . get_keys ( ) . m_account_address ;
std : : string header ;
std : : string header ;
header + = std : : string ( ( const char * ) & keys . m_spend_public_key , sizeof ( crypto : : public_key ) ) ;
header + = std : : string ( ( const char * ) & keys . m_spend_public_key , sizeof ( crypto : : public_key ) ) ;
header + = std : : string ( ( const char * ) & keys . m_view_public_key , sizeof ( crypto : : public_key ) ) ;
header + = std : : string ( ( const char * ) & keys . m_view_public_key , sizeof ( crypto : : public_key ) ) ;
PERF_TIMER ( export_outputs_encryption ) ;
std : : string ciphertext = encrypt_with_view_secret_key ( header + oss . str ( ) ) ;
std : : string ciphertext = encrypt_with_view_secret_key ( header + oss . str ( ) ) ;
return magic + ciphertext ;
return magic + ciphertext ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : import_outputs ( const std : : vector< tools : : wallet2 : : transfer_details > & outputs )
size_t wallet2 : : import_outputs ( const std : : pair< size_t , std : : vector< tools : : wallet2 : : transfer_details > > & outputs )
{
{
m_transfers . clear ( ) ;
PERF_TIMER ( import_outputs ) ;
m_transfers . reserve ( outputs . size ( ) ) ;
for ( size_t i = 0 ; i < outputs . size ( ) ; + + i )
THROW_WALLET_EXCEPTION_IF ( outputs . first > m_transfers . size ( ) , error : : wallet_internal_error ,
" Imported outputs omit more outputs that we know of " ) ;
const size_t offset = outputs . first ;
const size_t original_size = m_transfers . size ( ) ;
m_transfers . resize ( offset + outputs . second . size ( ) ) ;
for ( size_t i = 0 ; i < offset ; + + i )
m_transfers [ i ] . m_key_image_requested = false ;
for ( size_t i = 0 ; i < outputs . second . size ( ) ; + + i )
{
transfer_details td = outputs . second [ i ] ;
// skip those we've already imported, or which have different data
if ( i + offset < original_size )
{
{
transfer_details td = outputs [ i ] ;
// compare the data used to create the key image below
const transfer_details & org_td = m_transfers [ i + offset ] ;
if ( ! org_td . m_key_image_known )
goto process ;
# define CMPF(f) if (!(td.f == org_td.f)) goto process
CMPF ( m_txid ) ;
CMPF ( m_key_image ) ;
CMPF ( m_internal_output_index ) ;
# undef CMPF
if ( ! ( get_transaction_prefix_hash ( td . m_tx ) = = get_transaction_prefix_hash ( org_td . m_tx ) ) )
goto process ;
// copy anyway, since the comparison does not include ancillary fields which may have changed
m_transfers [ i + offset ] = std : : move ( td ) ;
continue ;
}
process :
// the hot wallet wouldn't have known about key images (except if we already exported them)
// the hot wallet wouldn't have known about key images (except if we already exported them)
cryptonote : : keypair in_ephemeral ;
cryptonote : : keypair in_ephemeral ;
THROW_WALLET_EXCEPTION_IF ( td . m_tx . vout . empty ( ) , error : : wallet_internal_error , " tx with no outputs at index " + boost : : lexical_cast < std : : string > ( i ) ) ;
THROW_WALLET_EXCEPTION_IF ( td . m_tx . vout . empty ( ) , error : : wallet_internal_error , " tx with no outputs at index " + boost : : lexical_cast < std : : string > ( i + offset ) ) ;
crypto : : public_key tx_pub_key = get_tx_pub_key_from_received_outs ( td ) ;
crypto : : public_key tx_pub_key = get_tx_pub_key_from_received_outs ( td ) ;
const std : : vector < crypto : : public_key > additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra ( td . m_tx ) ;
const std : : vector < crypto : : public_key > additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra ( td . m_tx ) ;
@ -11007,13 +11091,14 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
THROW_WALLET_EXCEPTION_IF ( ! r , error : : wallet_internal_error , " Failed to generate key image " ) ;
THROW_WALLET_EXCEPTION_IF ( ! r , error : : wallet_internal_error , " Failed to generate key image " ) ;
expand_subaddresses ( td . m_subaddr_index ) ;
expand_subaddresses ( td . m_subaddr_index ) ;
td . m_key_image_known = true ;
td . m_key_image_known = true ;
td . m_key_image_requested = true ;
td . m_key_image_partial = false ;
td . m_key_image_partial = false ;
THROW_WALLET_EXCEPTION_IF ( in_ephemeral . pub ! = out_key ,
THROW_WALLET_EXCEPTION_IF ( in_ephemeral . pub ! = out_key ,
error : : wallet_internal_error , " key_image generated ephemeral public key not matched with output_key at index " + boost : : lexical_cast < std : : string > ( i ) ) ;
error : : wallet_internal_error , " key_image generated ephemeral public key not matched with output_key at index " + boost : : lexical_cast < std : : string > ( i + offset ) ) ;
m_key_images [ td . m_key_image ] = m_transfers. size ( ) ;
m_key_images [ td . m_key_image ] = i + offset ;
m_pub_keys [ td . get_public_key ( ) ] = m_transfers. size ( ) ;
m_pub_keys [ td . get_public_key ( ) ] = i + offset ;
m_transfers .push_back ( std : : move ( td ) ) ;
m_transfers [i + offset ] = std : : move ( td ) ;
}
}
return m_transfers . size ( ) ;
return m_transfers . size ( ) ;
@ -11021,6 +11106,7 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : import_outputs_from_str ( const std : : string & outputs_st )
size_t wallet2 : : import_outputs_from_str ( const std : : string & outputs_st )
{
{
PERF_TIMER ( import_outputs_from_str ) ;
std : : string data = outputs_st ;
std : : string data = outputs_st ;
const size_t magiclen = strlen ( OUTPUT_EXPORT_FILE_MAGIC ) ;
const size_t magiclen = strlen ( OUTPUT_EXPORT_FILE_MAGIC ) ;
if ( data . size ( ) < magiclen | | memcmp ( data . data ( ) , OUTPUT_EXPORT_FILE_MAGIC , magiclen ) )
if ( data . size ( ) < magiclen | | memcmp ( data . data ( ) , OUTPUT_EXPORT_FILE_MAGIC , magiclen ) )
@ -11030,6 +11116,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
try
try
{
{
PERF_TIMER ( import_outputs_decrypt ) ;
data = decrypt_with_view_secret_key ( std : : string ( data , magiclen ) ) ;
data = decrypt_with_view_secret_key ( std : : string ( data , magiclen ) ) ;
}
}
catch ( const std : : exception & e )
catch ( const std : : exception & e )
@ -11056,7 +11143,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
std : : string body ( data , headerlen ) ;
std : : string body ( data , headerlen ) ;
std : : stringstream iss ;
std : : stringstream iss ;
iss < < body ;
iss < < body ;
std : : vector< tools : : wallet2 : : transfer_details > outputs ;
std : : pair< size_t , std : : vector< tools : : wallet2 : : transfer_details > > outputs ;
try
try
{
{
boost : : archive : : portable_binary_iarchive ar ( iss ) ;
boost : : archive : : portable_binary_iarchive ar ( iss ) ;
@ -11248,6 +11335,7 @@ void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key
m_key_images . erase ( td . m_key_image ) ;
m_key_images . erase ( td . m_key_image ) ;
td . m_key_image = get_multisig_composite_key_image ( n ) ;
td . m_key_image = get_multisig_composite_key_image ( n ) ;
td . m_key_image_known = true ;
td . m_key_image_known = true ;
td . m_key_image_requested = false ;
td . m_key_image_partial = false ;
td . m_key_image_partial = false ;
td . m_multisig_k = multisig_k [ n ] ;
td . m_multisig_k = multisig_k [ n ] ;
m_key_images [ td . m_key_image ] = n ;
m_key_images [ td . m_key_image ] = n ;