@ -894,6 +894,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_key_reuse_mitigation2 ( false ) ,
m_key_reuse_mitigation2 ( false ) ,
m_segregation_height ( 0 ) ,
m_segregation_height ( 0 ) ,
m_ignore_fractional_outputs ( true ) ,
m_ignore_fractional_outputs ( true ) ,
m_track_uses ( false ) ,
m_is_initialized ( false ) ,
m_is_initialized ( false ) ,
m_kdf_rounds ( kdf_rounds ) ,
m_kdf_rounds ( kdf_rounds ) ,
is_old_file_format ( false ) ,
is_old_file_format ( false ) ,
@ -1448,8 +1449,9 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has
}
}
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_new_transaction ( const crypto : : hash & txid , const cryptonote : : transaction & tx , const std : : vector < uint64_t > & o_indices , uint64_t height , uint64_t ts , bool miner_tx , bool pool , bool double_spend_seen , const tx_cache_data & tx_cache_data )
void wallet2 : : process_new_transaction ( const crypto : : hash & txid , const cryptonote : : transaction & tx , const std : : vector < uint64_t > & o_indices , uint64_t height , uint64_t ts , bool miner_tx , bool pool , bool double_spend_seen , const tx_cache_data & tx_cache_data , std : : map < std : : pair < uint64_t , uint64_t > , size_t > * output_tracker_cache )
{
{
PERF_TIMER ( process_new_transaction ) ;
// In this function, tx (probably) only contains the base information
// In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
// (that is, the prunable stuff may or may not be included)
if ( ! miner_tx & & ! pool )
if ( ! miner_tx & & ! pool )
@ -1686,6 +1688,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if ( ! m_multisig & & ! m_watch_only )
if ( ! m_multisig & & ! m_watch_only )
m_key_images [ td . m_key_image ] = m_transfers . size ( ) - 1 ;
m_key_images [ td . m_key_image ] = m_transfers . size ( ) - 1 ;
m_pub_keys [ tx_scan_info [ o ] . in_ephemeral . pub ] = m_transfers . size ( ) - 1 ;
m_pub_keys [ tx_scan_info [ o ] . in_ephemeral . pub ] = m_transfers . size ( ) - 1 ;
if ( output_tracker_cache )
( * output_tracker_cache ) [ std : : make_pair ( tx . vout [ o ] . amount , td . m_global_output_index ) ] = m_transfers . size ( ) - 1 ;
if ( m_multisig )
if ( m_multisig )
{
{
THROW_WALLET_EXCEPTION_IF ( ! m_multisig_rescan_k & & m_multisig_rescan_info ,
THROW_WALLET_EXCEPTION_IF ( ! m_multisig_rescan_k & & m_multisig_rescan_info ,
@ -1751,6 +1755,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td . m_mask = rct : : identity ( ) ;
td . m_mask = rct : : identity ( ) ;
td . m_rct = false ;
td . m_rct = false ;
}
}
if ( output_tracker_cache )
( * output_tracker_cache ) [ std : : make_pair ( tx . vout [ o ] . amount , td . m_global_output_index ) ] = kit - > second ;
if ( m_multisig )
if ( m_multisig )
{
{
THROW_WALLET_EXCEPTION_IF ( ! m_multisig_rescan_k & & m_multisig_rescan_info ,
THROW_WALLET_EXCEPTION_IF ( ! m_multisig_rescan_k & & m_multisig_rescan_info ,
@ -1782,11 +1788,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
{
if ( in . type ( ) ! = typeid ( cryptonote : : txin_to_key ) )
if ( in . type ( ) ! = typeid ( cryptonote : : txin_to_key ) )
continue ;
continue ;
auto it = m_key_images . find ( boost : : get < cryptonote : : txin_to_key > ( in ) . k_image ) ;
const cryptonote : : txin_to_key & in_to_key = boost : : get < cryptonote : : txin_to_key > ( in ) ;
auto it = m_key_images . find ( in_to_key . k_image ) ;
if ( it ! = m_key_images . end ( ) )
if ( it ! = m_key_images . end ( ) )
{
{
transfer_details & td = m_transfers [ it - > second ] ;
transfer_details & td = m_transfers [ it - > second ] ;
uint64_t amount = boost: : get < cryptonote : : tx in_to_key> ( in ) . amount ;
uint64_t amount = in_to_key. amount ;
if ( amount > 0 )
if ( amount > 0 )
{
{
if ( amount ! = td . amount ( ) )
if ( amount ! = td . amount ( ) )
@ -1817,6 +1824,34 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback - > on_money_spent ( height , txid , tx , amount , tx , td . m_subaddr_index ) ;
m_callback - > on_money_spent ( height , txid , tx , amount , tx , td . m_subaddr_index ) ;
}
}
}
}
if ( ! pool & & m_track_uses )
{
PERF_TIMER ( track_uses ) ;
const uint64_t amount = in_to_key . amount ;
std : : vector < uint64_t > offsets = cryptonote : : relative_output_offsets_to_absolute ( in_to_key . key_offsets ) ;
if ( output_tracker_cache )
{
for ( uint64_t offset : offsets )
{
const std : : map < std : : pair < uint64_t , uint64_t > , size_t > : : const_iterator i = output_tracker_cache - > find ( std : : make_pair ( amount , offset ) ) ;
if ( i ! = output_tracker_cache - > end ( ) )
{
size_t idx = i - > second ;
THROW_WALLET_EXCEPTION_IF ( idx > = m_transfers . size ( ) , error : : wallet_internal_error , " Output tracker cache index out of range " ) ;
m_transfers [ idx ] . m_uses . push_back ( std : : make_pair ( height , txid ) ) ;
}
}
}
else for ( transfer_details & td : m_transfers )
{
if ( amount ! = in_to_key . amount )
continue ;
for ( uint64_t offset : offsets )
if ( offset = = td . m_global_output_index )
td . m_uses . push_back ( std : : make_pair ( height , txid ) ) ;
}
}
}
}
uint64_t fee = miner_tx ? 0 : tx . version = = 1 ? tx_money_spent_in_ins - get_outs_money_amount ( tx ) : tx . rct_signatures . txnFee ;
uint64_t fee = miner_tx ? 0 : tx . version = = 1 ? tx_money_spent_in_ins - get_outs_money_amount ( tx ) : tx . rct_signatures . txnFee ;
@ -1999,7 +2034,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
add_rings ( tx ) ;
add_rings ( tx ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_new_blockchain_entry ( const cryptonote : : block & b , const cryptonote : : block_complete_entry & bche , const parsed_block & parsed_block , const crypto : : hash & bl_id , uint64_t height , const std : : vector < tx_cache_data > & tx_cache_data , size_t tx_cache_data_offset )
void wallet2 : : process_new_blockchain_entry ( const cryptonote : : block & b , const cryptonote : : block_complete_entry & bche , const parsed_block & parsed_block , const crypto : : hash & bl_id , uint64_t height , const std : : vector < tx_cache_data > & tx_cache_data , size_t tx_cache_data_offset , std : : map < std : : pair < uint64_t , uint64_t > , size_t > * output_tracker_cache )
{
{
THROW_WALLET_EXCEPTION_IF ( bche . txs . size ( ) + 1 ! = parsed_block . o_indices . indices . size ( ) , error : : wallet_internal_error ,
THROW_WALLET_EXCEPTION_IF ( bche . txs . size ( ) + 1 ! = parsed_block . o_indices . indices . size ( ) , error : : wallet_internal_error ,
" block transactions= " + std : : to_string ( bche . txs . size ( ) ) +
" block transactions= " + std : : to_string ( bche . txs . size ( ) ) +
@ -2012,7 +2047,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
{
{
TIME_MEASURE_START ( miner_tx_handle_time ) ;
TIME_MEASURE_START ( miner_tx_handle_time ) ;
if ( m_refresh_type ! = RefreshNoCoinbase )
if ( m_refresh_type ! = RefreshNoCoinbase )
process_new_transaction ( get_transaction_hash ( b . miner_tx ) , b . miner_tx , parsed_block . o_indices . indices [ 0 ] . indices , height , b . timestamp , true , false , false , tx_cache_data [ tx_cache_data_offset ] );
process_new_transaction ( get_transaction_hash ( b . miner_tx ) , b . miner_tx , parsed_block . o_indices . indices [ 0 ] . indices , height , b . timestamp , true , false , false , tx_cache_data [ tx_cache_data_offset ] , output_tracker_cache );
+ + tx_cache_data_offset ;
+ + tx_cache_data_offset ;
TIME_MEASURE_FINISH ( miner_tx_handle_time ) ;
TIME_MEASURE_FINISH ( miner_tx_handle_time ) ;
@ -2021,7 +2056,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
THROW_WALLET_EXCEPTION_IF ( bche . txs . size ( ) ! = parsed_block . txes . size ( ) , error : : wallet_internal_error , " Wrong amount of transactions for block " ) ;
THROW_WALLET_EXCEPTION_IF ( bche . txs . size ( ) ! = parsed_block . txes . size ( ) , error : : wallet_internal_error , " Wrong amount of transactions for block " ) ;
for ( size_t idx = 0 ; idx < b . tx_hashes . size ( ) ; + + idx )
for ( size_t idx = 0 ; idx < b . tx_hashes . size ( ) ; + + idx )
{
{
process_new_transaction ( b . tx_hashes [ idx ] , parsed_block . txes [ idx ] , parsed_block . o_indices . indices [ idx + 1 ] . indices , height , b . timestamp , false , false , false , tx_cache_data [ tx_cache_data_offset + + ] );
process_new_transaction ( b . tx_hashes [ idx ] , parsed_block . txes [ idx ] , parsed_block . o_indices . indices [ idx + 1 ] . indices , height , b . timestamp , false , false , false , tx_cache_data [ tx_cache_data_offset + + ] , output_tracker_cache );
}
}
TIME_MEASURE_FINISH ( txs_handle_time ) ;
TIME_MEASURE_FINISH ( txs_handle_time ) ;
m_last_block_reward = cryptonote : : get_outs_money_amount ( b . miner_tx ) ;
m_last_block_reward = cryptonote : : get_outs_money_amount ( b . miner_tx ) ;
@ -2119,7 +2154,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
hashes = std : : move ( res . m_block_ids ) ;
hashes = std : : move ( res . m_block_ids ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_parsed_blocks ( uint64_t start_height , const std : : vector < cryptonote : : block_complete_entry > & blocks , const std : : vector < parsed_block > & parsed_blocks , uint64_t & blocks_added )
void wallet2 : : process_parsed_blocks ( uint64_t start_height , const std : : vector < cryptonote : : block_complete_entry > & blocks , const std : : vector < parsed_block > & parsed_blocks , uint64_t & blocks_added , std : : map < std : : pair < uint64_t , uint64_t > , size_t > * output_tracker_cache )
{
{
size_t current_index = start_height ;
size_t current_index = start_height ;
blocks_added = 0 ;
blocks_added = 0 ;
@ -2226,7 +2261,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
if ( current_index > = m_blockchain . size ( ) )
if ( current_index > = m_blockchain . size ( ) )
{
{
process_new_blockchain_entry ( bl , blocks [ i ] , parsed_blocks [ i ] , bl_id , current_index , tx_cache_data , tx_cache_data_offset );
process_new_blockchain_entry ( bl , blocks [ i ] , parsed_blocks [ i ] , bl_id , current_index , tx_cache_data , tx_cache_data_offset , output_tracker_cache );
+ + blocks_added ;
+ + blocks_added ;
}
}
else if ( bl_id ! = m_blockchain [ current_index ] )
else if ( bl_id ! = m_blockchain [ current_index ] )
@ -2238,7 +2273,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
string_tools : : pod_to_hex ( m_blockchain [ current_index ] ) ) ;
string_tools : : pod_to_hex ( m_blockchain [ current_index ] ) ) ;
detach_blockchain ( current_index ) ;
detach_blockchain ( current_index ) ;
process_new_blockchain_entry ( bl , blocks [ i ] , parsed_blocks [ i ] , bl_id , current_index , tx_cache_data , tx_cache_data_offset );
process_new_blockchain_entry ( bl , blocks [ i ] , parsed_blocks [ i ] , bl_id , current_index , tx_cache_data , tx_cache_data_offset , output_tracker_cache );
}
}
else
else
{
{
@ -2670,6 +2705,17 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
return true ;
return true ;
}
}
//----------------------------------------------------------------------------------------------------
std : : shared_ptr < std : : map < std : : pair < uint64_t , uint64_t > , size_t > > wallet2 : : create_output_tracker_cache ( ) const
{
std : : shared_ptr < std : : map < std : : pair < uint64_t , uint64_t > , size_t > > cache { new std : : map < std : : pair < uint64_t , uint64_t > , size_t > ( ) } ;
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
const transfer_details & td = m_transfers [ i ] ;
( * cache ) [ std : : make_pair ( td . is_rct ( ) ? 0 : td . amount ( ) , td . m_global_output_index ) ] = i ;
}
return cache ;
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
void wallet2 : : refresh ( bool trusted_daemon , uint64_t start_height , uint64_t & blocks_fetched , bool & received_money )
void wallet2 : : refresh ( bool trusted_daemon , uint64_t start_height , uint64_t & blocks_fetched , bool & received_money )
{
{
@ -2717,6 +2763,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
std : : vector < cryptonote : : block_complete_entry > blocks ;
std : : vector < cryptonote : : block_complete_entry > blocks ;
std : : vector < parsed_block > parsed_blocks ;
std : : vector < parsed_block > parsed_blocks ;
bool refreshed = false ;
bool refreshed = false ;
std : : shared_ptr < std : : map < std : : pair < uint64_t , uint64_t > , size_t > > output_tracker_cache ;
// pull the first set of blocks
// pull the first set of blocks
get_short_chain_history ( short_chain_history , ( m_first_refresh_done | | trusted_daemon ) ? 1 : FIRST_REFRESH_GRANULARITY ) ;
get_short_chain_history ( short_chain_history , ( m_first_refresh_done | | trusted_daemon ) ? 1 : FIRST_REFRESH_GRANULARITY ) ;
@ -2770,7 +2817,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
{
try
try
{
{
process_parsed_blocks ( blocks_start_height , blocks , parsed_blocks , added_blocks );
process_parsed_blocks ( blocks_start_height , blocks , parsed_blocks , added_blocks , output_tracker_cache . get ( ) );
}
}
catch ( const tools : : error : : out_of_hashchain_bounds_error & )
catch ( const tools : : error : : out_of_hashchain_bounds_error & )
{
{
@ -2813,6 +2860,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
throw std : : runtime_error ( " proxy exception in refresh thread " ) ;
throw std : : runtime_error ( " proxy exception in refresh thread " ) ;
}
}
// if we've got at least 10 blocks to refresh, assume we're starting
// a long refresh, and setup a tracking output cache if we need to
if ( m_track_uses & & ! output_tracker_cache & & next_blocks . size ( ) > = 10 )
output_tracker_cache = create_output_tracker_cache ( ) ;
// switch to the new blocks from the daemon
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height ;
blocks_start_height = next_blocks_start_height ;
blocks = std : : move ( next_blocks ) ;
blocks = std : : move ( next_blocks ) ;
@ -3182,6 +3234,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2 . SetInt ( m_ignore_fractional_outputs ? 1 : 0 ) ;
value2 . SetInt ( m_ignore_fractional_outputs ? 1 : 0 ) ;
json . AddMember ( " ignore_fractional_outputs " , value2 , json . GetAllocator ( ) ) ;
json . AddMember ( " ignore_fractional_outputs " , value2 , json . GetAllocator ( ) ) ;
value2 . SetInt ( m_track_uses ? 1 : 0 ) ;
json . AddMember ( " track_uses " , value2 , json . GetAllocator ( ) ) ;
value2 . SetUint ( m_subaddress_lookahead_major ) ;
value2 . SetUint ( m_subaddress_lookahead_major ) ;
json . AddMember ( " subaddress_lookahead_major " , value2 , json . GetAllocator ( ) ) ;
json . AddMember ( " subaddress_lookahead_major " , value2 , json . GetAllocator ( ) ) ;
@ -3328,6 +3383,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_key_reuse_mitigation2 = true ;
m_key_reuse_mitigation2 = true ;
m_segregation_height = 0 ;
m_segregation_height = 0 ;
m_ignore_fractional_outputs = true ;
m_ignore_fractional_outputs = true ;
m_track_uses = false ;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR ;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR ;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR ;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR ;
m_original_keys_available = false ;
m_original_keys_available = false ;
@ -3480,6 +3536,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = field_segregation_height ;
m_segregation_height = field_segregation_height ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , ignore_fractional_outputs , int , Int , false , true ) ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , ignore_fractional_outputs , int , Int , false , true ) ;
m_ignore_fractional_outputs = field_ignore_fractional_outputs ;
m_ignore_fractional_outputs = field_ignore_fractional_outputs ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , track_uses , int , Int , false , false ) ;
m_track_uses = field_track_uses ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , subaddress_lookahead_major , uint32_t , Uint , false , SUBADDRESS_LOOKAHEAD_MAJOR ) ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , subaddress_lookahead_major , uint32_t , Uint , false , SUBADDRESS_LOOKAHEAD_MAJOR ) ;
m_subaddress_lookahead_major = field_subaddress_lookahead_major ;
m_subaddress_lookahead_major = field_subaddress_lookahead_major ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , subaddress_lookahead_minor , uint32_t , Uint , false , SUBADDRESS_LOOKAHEAD_MINOR ) ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , subaddress_lookahead_minor , uint32_t , Uint , false , SUBADDRESS_LOOKAHEAD_MINOR ) ;
@ -5266,6 +5324,10 @@ void wallet2::rescan_blockchain(bool hard, bool refresh)
m_transfers . clear ( ) ;
m_transfers . clear ( ) ;
m_key_images . clear ( ) ;
m_key_images . clear ( ) ;
m_pub_keys . clear ( ) ;
m_pub_keys . clear ( ) ;
m_unconfirmed_txs . clear ( ) ;
m_payments . clear ( ) ;
m_confirmed_txs . clear ( ) ;
m_unconfirmed_payments . clear ( ) ;
m_scanned_pool_txs [ 0 ] . clear ( ) ;
m_scanned_pool_txs [ 0 ] . clear ( ) ;
m_scanned_pool_txs [ 1 ] . clear ( ) ;
m_scanned_pool_txs [ 1 ] . clear ( ) ;